Skip to content

Add Jmespath expression function#1790

Open
TomasEng wants to merge 3 commits into
support-objects-in-expressionsfrom
add-jmespath-function
Open

Add Jmespath expression function#1790
TomasEng wants to merge 3 commits into
support-objects-in-expressionsfrom
add-jmespath-function

Conversation

@TomasEng
Copy link
Copy Markdown
Contributor

@TomasEng TomasEng commented Jun 2, 2026

Important

This pull request is stacked upon #1777, which should be merged first.

Description

This pull request adds support for Jmespath queries in our expression language. This inludes a new Nuget dependency, JmesPath.Net.

Here is the corresponding implementation in frontend: Altinn/altinn-studio#19028

Related Issue(s)

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added: The tests mainly cover different kinds of input and output types and error cases to make sure we account for them in our implementation. There are also some tests that focus on the bahaviour of the Jmespath language, but it is not necessary to go into depth on that since it is the reponsibility of the JmesPath.Net package.
  • All tests run green

Documentation

Summary by CodeRabbit

  • New Features

    • Added JMESPath as a new layout expression function, enabling advanced data querying and transformation capabilities within expressions.
  • Tests

    • Added comprehensive test cases for JMESPath functionality, including property lookups, filtering, and projection operations.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

📝 Walkthrough

Walkthrough

This pull request adds JMESPath query evaluation as a new expression function type. The implementation introduces a NuGet dependency, defines a new enum member, creates a JMESPath evaluator class, adds JSON-to-ExpressionValue conversion, integrates the evaluator into the expression pipeline, and provides comprehensive test coverage with both positive and negative test cases.

Changes

JMESPath Expression Function

Layer / File(s) Summary
Package dependency and function contract
Directory.Packages.props, src/Altinn.App.Core/Altinn.App.Core.csproj, src/Altinn.App.Core/Models/Expressions/ExpressionFunction.cs, test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
JmesPath.Net package (1.1.0) is declared in the central package manifest and added as a project dependency. The ExpressionFunction enum gains a new jmespath member, extending the set of supported function types.
JSON element conversion utility
src/Altinn.App.Core/Internal/Expressions/ExpressionValue.cs, test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
ExpressionValue gains a public static factory method FromJsonElement that converts JsonElement values (returned by JMESPath) into the appropriate ExpressionValue variant by inspecting the ValueKind and deserializing objects/arrays.
JMESPath evaluator implementation
src/Altinn.App.Core/Internal/Expressions/JmespathFunctionEvaluator.cs
A new internal JmespathFunctionEvaluator class validates a two-argument expression (data and query string), executes the JMESPath query via JmesPath.Transform, and converts the JSON string result back to ExpressionValue using FromJsonElement. Exceptions are wrapped in ExpressionEvaluatorTypeErrorException.
Expression evaluator integration
src/Altinn.App.Core/Internal/Expressions/ExpressionEvaluator.cs
The expression evaluator's switch statement adds a handler for ExpressionFunction.jmespath that delegates to a new private Jmespath helper method, which instantiates and invokes JmespathFunctionEvaluator.
Test coverage
test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/TestFunctions.cs, test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/jmespath/jmespath.json
A new parameterized test method Jmespath_Theory is added to run shared test cases. The jmespath.json test specification includes positive cases (property lookups, object/list returns, projections, filtering, current-node tokens, literals, function calls) and negative cases (invalid queries, missing arguments, null/non-string queries) with expected error messages.

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding JMESPath as a new expression function with supporting implementation across multiple files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-jmespath-function

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@TomasEng TomasEng added feature Label Pull requests with new features. Used when generation releasenotes backport-ignore This PR is a new feature and should not be cherry-picked onto release branches labels Jun 2, 2026
@TomasEng TomasEng force-pushed the add-jmespath-function branch 2 times, most recently from 323dac5 to 85a7a1a Compare June 2, 2026 12:44
@TomasEng TomasEng force-pushed the add-jmespath-function branch from 85a7a1a to 1ed1350 Compare June 2, 2026 12:53
@TomasEng TomasEng added the squad/data Issues that belongs to the named squad. label Jun 2, 2026
@TomasEng TomasEng moved this to 👷 In progress in Team Altinn Studio Jun 2, 2026
@TomasEng
Copy link
Copy Markdown
Contributor Author

TomasEng commented Jun 2, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/Altinn.App.Core/Internal/Expressions/ExpressionValue.cs (1)

206-209: 💤 Low value

Fallback expressions throw instead of returning a null value.

Null.String, Null.Dictionary, and Null.Array invoke the .String/.Dictionary/.Array accessors on a Null-kind value, all of which throw InvalidCastException. These branches are currently unreachable (GetString()/Deserialize won't return null for a String/Object/Array kind), but if they ever were hit they'd throw a misleading cast error rather than yielding a null value. Consider returning ExpressionValue.Null instead.

♻️ Suggested change
-            JsonValueKind.String => element.GetString() ?? Null.String,
+            JsonValueKind.String => element.GetString() ?? Null,
             JsonValueKind.Number => element.GetDouble(),
-            JsonValueKind.Object => element.Deserialize<JsonObject>() ?? Null.Dictionary,
-            JsonValueKind.Array => element.Deserialize<JsonArray>() ?? Null.Array,
+            JsonValueKind.Object => element.Deserialize<JsonObject>() is { } o ? new ExpressionValue(o) : Null,
+            JsonValueKind.Array => element.Deserialize<JsonArray>() is { } a ? new ExpressionValue(a) : Null,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Altinn.App.Core/Internal/Expressions/ExpressionValue.cs` around lines 206
- 209, The switch branches that fall back to
Null.String/Null.Dictionary/Null.Array should return the ExpressionValue.Null
sentinel instead to avoid invoking the .String/.Dictionary/.Array accessors
(which throw InvalidCastException); update the JsonValueKind.String,
JsonValueKind.Object and JsonValueKind.Array cases in the ExpressionValue
conversion logic to use ExpressionValue.Null as the fallback (instead of
Null.String/Null.Dictionary/Null.Array) so a true null-expression value is
returned if those fallbacks are ever hit.
src/Altinn.App.Core/Internal/Expressions/JmespathFunctionEvaluator.cs (1)

6-6: 💤 Low value

Mark the evaluator sealed.

As per coding guidelines, "Use sealed for classes unless inheritance is a valid use-case". This class isn't designed for inheritance.

♻️ Suggested change
-internal class JmespathFunctionEvaluator
+internal sealed class JmespathFunctionEvaluator
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Altinn.App.Core/Internal/Expressions/JmespathFunctionEvaluator.cs` at
line 6, The class JmespathFunctionEvaluator should be declared sealed to prevent
inheritance; update the class declaration for JmespathFunctionEvaluator by
adding the sealed modifier (i.e., change the class declaration from "internal
class JmespathFunctionEvaluator" to "internal sealed class
JmespathFunctionEvaluator") and run the build to ensure no code relies on
inheriting from this class.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Altinn.App.Core/Internal/Expressions/JmespathFunctionEvaluator.cs`:
- Around line 18-21: The error message in JmespathFunctionEvaluator's argument
validation throws ExpressionEvaluatorTypeErrorException for _args[1] but
mistakenly interpolates _args[0]; update the exception message to reference the
offending argument (_args[1]) and include helpful info (e.g., its ValueKind or
ToString()) so the diagnostic reports the correct query argument when
_args[1].ValueKind != JsonValueKind.String.

---

Nitpick comments:
In `@src/Altinn.App.Core/Internal/Expressions/ExpressionValue.cs`:
- Around line 206-209: The switch branches that fall back to
Null.String/Null.Dictionary/Null.Array should return the ExpressionValue.Null
sentinel instead to avoid invoking the .String/.Dictionary/.Array accessors
(which throw InvalidCastException); update the JsonValueKind.String,
JsonValueKind.Object and JsonValueKind.Array cases in the ExpressionValue
conversion logic to use ExpressionValue.Null as the fallback (instead of
Null.String/Null.Dictionary/Null.Array) so a true null-expression value is
returned if those fallbacks are ever hit.

In `@src/Altinn.App.Core/Internal/Expressions/JmespathFunctionEvaluator.cs`:
- Line 6: The class JmespathFunctionEvaluator should be declared sealed to
prevent inheritance; update the class declaration for JmespathFunctionEvaluator
by adding the sealed modifier (i.e., change the class declaration from "internal
class JmespathFunctionEvaluator" to "internal sealed class
JmespathFunctionEvaluator") and run the build to ensure no code relies on
inheriting from this class.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c7a28c66-4241-4568-b5dd-f57e42d5908f

📥 Commits

Reviewing files that changed from the base of the PR and between a5fa375 and 1ed1350.

📒 Files selected for processing (9)
  • Directory.Packages.props
  • src/Altinn.App.Core/Altinn.App.Core.csproj
  • src/Altinn.App.Core/Internal/Expressions/ExpressionEvaluator.cs
  • src/Altinn.App.Core/Internal/Expressions/ExpressionValue.cs
  • src/Altinn.App.Core/Internal/Expressions/JmespathFunctionEvaluator.cs
  • src/Altinn.App.Core/Models/Expressions/ExpressionFunction.cs
  • test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/TestFunctions.cs
  • test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/jmespath/jmespath.json
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 3, 2026

@TomasEng TomasEng marked this pull request as ready for review June 3, 2026 12:00
@TomasEng TomasEng moved this from 👷 In progress to 🔎 In review in Team Altinn Studio Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-ignore This PR is a new feature and should not be cherry-picked onto release branches feature Label Pull requests with new features. Used when generation releasenotes squad/data Issues that belongs to the named squad.

Projects

Status: 🔎 In review

Development

Successfully merging this pull request may close these issues.

1 participant