https://github.com/gravitee-io/gravitee-expression-language
Expression Language used by Gravitee.io Components
https://github.com/gravitee-io/gravitee-expression-language
product-platform security-scan
Last synced: 20 days ago
JSON representation
Expression Language used by Gravitee.io Components
- Host: GitHub
- URL: https://github.com/gravitee-io/gravitee-expression-language
- Owner: gravitee-io
- License: apache-2.0
- Created: 2017-09-20T14:29:42.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2025-04-03T15:03:08.000Z (about 1 month ago)
- Last Synced: 2025-04-03T16:22:15.699Z (about 1 month ago)
- Topics: product-platform, security-scan
- Language: Java
- Size: 327 KB
- Stars: 3
- Watchers: 27
- Forks: 4
- Open Issues: 2
-
Metadata Files:
- Readme: README.adoc
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.adoc
- License: LICENSE.txt
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
= Gravitee Expression Language
image:https://img.shields.io/badge/License-Apache%202.0-blue.svg["License", link="https://github.com/gravitee-io/gravitee-expression-language/blob/master/LICENSE.txt"]
image:https://img.shields.io/badge/semantic--release-conventional%20commits-e10079?logo=semantic-release["Releases", link="https://github.com/gravitee-io/gravitee-expression-language/releases"]
image:https://circleci.com/gh/gravitee-io/gravitee-expression-language.svg?style=svg["CircleCI", link="https://circleci.com/gh/gravitee-io/gravitee-expression-language"]
image:https://f.hubspotusercontent40.net/hubfs/7600448/gravitee-github-button.jpg["Join the community forum", link="https://community.gravitee.io?utm_source=readme", height=20]== Description
This project provides a custom template engine for parsing https://documentation.gravitee.io/apim/getting-started/gravitee-expression-language[Expression Language] elements.
== Usage
=== Basic Usage
EL expressions can be evaluated using a `TemplateEngine` instance:
[source,java]
----
TemplateEngine engine = TemplateEngine.templateEngine();
----To pass variables to the `TemplateEngine`, use its context:
[source,java]
----
TemplateEngine engine = TemplateEngine.templateEngine();
TemplateContext context = engine.getTemplateContext();context.setVariable("myVar", "myValue");
----The context remains available for any evaluated expression. You can evaluate an EL expression against your `TemplateEngine` instance. The first argument is the EL expression, and the second is the expected result type.
[source,java]
----
TemplateEngine engine = TemplateEngine.templateEngine();
TemplateContext context = engine.getTemplateContext();context.setVariable("myVar", "myValue");
engine.evalNow("{#myVar}", String.class); // Result: "myValue"
engine.evalNow("{#myVar.isEmpty()}", Boolean.class); // Result: false
----IMPORTANT: Ensure the expected result type matches the evaluated expression.
=== Calling Functions
A variable can be of any type and may expose functions:
[source,java]
----
// Assign a user variable.
context.setVariable("myUser", new User("firstname", "lastname"));engine.eval("{#myUser.getDisplayName()}", String.class); // Calls getDisplayName() on the User instance.
----IMPORTANT: You must explicitly declare the `User.getDisplayName()` function in the whitelist of authorized methods. See <> for details.
=== Reactive Usage
To evaluate an EL expression reactively, use `eval()` instead of `evalNow()`.
IMPORTANT: evalNow() is not strictly equivalent to `eval()` as it does not rely on reactive stack and does not support deferred variables or reactive functions. Hence, `evalNow()` is not suitable for advanced usages such as evaluating an expression based on the content of the request or response body (e.g: `{#request.content}`). However, `evalNow()` remains suitable when evaluating expressions outside a request processing (e.g. API or Security Domain deployment, connector initialization, ...).
[source,java]
----
context.setVariable("myVar", "myValue");engine.eval("{#myVar}", String.class); // Returns Maybe
engine.eval("{#myVar.isEmpty()}", Boolean.class); // Returns Maybe
----==== Deferred Variables
The `TemplateEngine` allows assigning **deferred variables**, useful for injecting values that must be fetched before evaluation. A deferred variable can be a `Maybe` or a `Single`.
[source,java]
----
// Assigns a variable from an HTTP call.
context.setDeferredVariable("myVar", httpClient.fetch("https://somewhere.com").flatMap(result::message));engine.eval("{#myVar}", String.class); // Resolves myVar before evaluation.
----TIP: A deferred variable is evaluated **only if used** in an expression. In the above example, `{#myVar}` triggers the HTTP call, while `{#anotherVar}` does not.
==== Evaluating Functions Returning `Maybe` or `Single`
If you need to evaluate a function that returns a `Maybe` or `Single`, you must inject a specific implementation of `DeferredFunctionHolder` in the context:
[source,java]
----
import io.gravitee.el.spel.context.DeferredFunctionHolder;class MyDeferredFunctionHolder implements DeferredFunctionHolder {
public Maybe resolve(String param) {
// ... return a Maybe.
}public Maybe doSomethingReactive() {
// ... return a Maybe.
}
};// Assigns myHolder in the context.
context.setDeferredFunctionHolderVariable("myHolder", new MyDeferredFunctionHolder());engine.eval("{#myHolder.resolve('something')}", String.class); // Handles the Maybe returned by the function call and evaluates the final string.
engine.eval("{#myHolder.doSomethingReactive()}", String.class); // Handles the Maybe returned by the function call and evaluates the final string.
----== EL Sandbox
The EL Template Engine includes a built-in sandbox feature, allowing safe execution of EL expressions. The sandbox operates based on a predefined whitelist of allowed methods, fields, and constructors.
=== Whitelist Configuration
The sandbox provides the following configuration options:
* `el.whitelist.mode`: Defines whether to `append` or `replace` the built-in whitelist.
** `append` (default) - Adds new whitelisted definitions while keeping existing ones.
** `replace` - Replaces the built-in list entirely (use with caution).TIP: Always use `append` unless you are certain you need `replace`.
* `el.whitelist.list`: Specifies additional methods, constructors, fields, or annotations to allow.
** Prefix with `method` to allow a specific method (full signature required).
** Prefix with `class` to allow an entire class (all methods, constructors, and fields will be accessible).==== Example Configuration
[source,yaml]
----
el:
whitelist:
mode: append
list:
- method java.time.format.DateTimeFormatter ofLocalizedDate java.time.format.FormatStyle
- class java.time.format.DateTimeFormatter
----WARNING: Be cautious when allowing entire classes or methods. Some classes may expose unintended methods, creating security risks.