Skip to content

Latest commit

 

History

History
346 lines (239 loc) · 25.7 KB

gravitee-expression-language.md

File metadata and controls

346 lines (239 loc) · 25.7 KB

Gravitee Expression Language

Overview

Gravitee Expression Language (EL) is used to query and manipulate object graphs and dynamically configure various aspects and policies of an API. It allows you to reference values from the current API transaction to use expressions to create dynamic filters, routing rules, and policies that respond to specific conditions or parameters.

EL is an extended version of the Spring Expression Language (SpEL) that augments standard SpEL capabilities by providing additional object properties inside the expression language context. As an extension of SpEL, all capabilities detailed in the SpEL documentation are available in EL. However, Gravitee has implemented customizations that are detailed below.

{% hint style="info" %} Object properties

Custom properties and attributes have special meanings in the Gravitee ecosystem:

  • Custom Properties: Defined at the API level and read-only during the Gateway's execution of an API transaction. You can learn more about how to set an API's custom properties here.
  • Attributes: Scoped to the current API transaction and can be manipulated during the execution phase through the assign-attributes policy. Attributes are used to attach additional information to a request or message via a variable that is dropped after the API transaction is completed. {% endhint %}

The following sections define the scope and usage of EL:

Basic usage

The information below summarizes:

  • Object properties added to the EL context
  • How attributes are accessed for v4 and v2 APIs
  • Commonly used operators and functions

{% tabs %} {% tab title="Syntax" %} Expressions

Expressions in Gravitee are enclosed in curly braces {} and begin with the # symbol. Both dot notation and bracket notation are supported for accessing the properties of an object.

Example: {#context.attributes['user'].email}

{% hint style="info" %} Dot notation vs bracket notation

Please note that dot notation will not work with special characters:

{#request.headers.my-header} <- This will result in an error

Bracket notation should be used for property names that include a space or a hyphen, or start with a number:

{#request.headers['my-header']} {% endhint %}

Lists

Expressions can be used to assign lists, e.g., {({'admin', 'writer'})}

  1. The outer enclosing brackets start and end the EL expression
  2. The parentheses indicates an object is being instantiated
  3. The list comprises the inner brackets and enclosed values, e.g., {'admin', 'writer'} {% endtab %}

{% tab title="Object properties" %} EL allows you to reference certain values injected into the EL context as object properties. The available object properties will be further detailed in later sections. EL adds the following root-level object properties:

  • {#api.properties}: Contains custom properties defined by the API publisher for that Gateway API.
  • {#dictionaries}: Contains custom dictionaries defined by the API publisher for that Gateway API.
  • {#endpoints}: Contains information about the Gateway API's respective endpoints.
  • {#request}: Contains information about the current API request.
  • {#response}: Contains information about the current API response.
  • {#message}: Contains information about the current API message.
  • {#node} : Contains information about the node hosting the instance of the Gateway handling the API transaction. {% endtab %}

{% tab title="Attributes" %} The attributes object property contains attributes that are automatically created by the APIM Gateway during an API transaction or added during the execution phase through the Assign Attributes policy. However, attributes fall into one of two categories based on API type:

  • {#context.attributes}: Contains attributes associated with v2 APIs or v4 Proxy APIs. A v4 Proxy API is created using the Proxy upstream protocol method.
  • {#message.attributes}: Contains attributes associated with v4 Message APIs. These APIs are created using the Introspect messages from event-driven backend method.

See the v4 API creation wizard for more details. {% endtab %}

{% tab title="Operators" %} EL supports various operators, such as arithmetic, logical, comparison, and ternary operators. Examples of commonly used operators in Gravitee include:

  • Arithmetic operators: +, -, *, /
  • Logical operators: && (logical and), || (logical or), ! (logical not)
  • Comparison operators: ==, !=, <, <=, >, >=
  • Ternary operators: condition ? expression1 : expression2 {% endtab %}

{% tab title="Functions" %} EL provides a variety of built-in functions to manipulate and transform data in expressions. Examples of commonly used functions in Gravitee include:

  • String functions: length(), substring(), replace()
  • #jsonPath: Evaluates a jsonPath on a specified object. This function invokes JsonPathUtils.evaluate(…​), which delegates to the Jayway JsonPath library. The best way to learn jsonPath syntax is by using the online evaluator.
  • #xpath: To evaluate an xpath on some provided object. For more information regarding XML and XPath, see XML Support - Dealing with XML Payloads in the SpEL documentation.

jsonPath example

As an example of how jsonPath can be used with EL, suppose you have a JSON payload in the request body that contains the following data:

{
  "store": {
    "book": [
      {
        "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      {
        "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ]
  }
}

To extract the value of the price property for the book with title "The Lord of the Rings," you can use the following expression:

{#jsonPath(#request.content, "$.store.book[?(@.title=='The Lord of the Rings')].price")} {% endtab %} {% endtabs %}

APIs

Using EL, you can access information about an API transaction through several root-level objects that are injected into the EL context: custom properties, dictionaries, and endpoints.

{% tabs %} {% tab title="Custom properties" %} As an API publisher, you can define custom properties for your API. These properties are automatically injected into the expression language context and can be referenced during an API transaction from the {#api.properties} root-level object property.

Examples

  • Get the value of the property my-property defined in an API's custom properties using {#api.properties['my-property']}
  • Get the value of the property my-secret defined and encrypted in an API's custom properties using {#api.properties['my-secret']} to pass a secured property to your backend

{% hint style="info" %} Encrypted custom properties

When accessing an encrypted custom property, Gravitee's Gateway will automatically manage the decryption and provide a plain text value. {% endhint %} {% endtab %}

{% tab title="Dictionaries" %} Dictionaries work similarly to custom properties, but you need to specify the dictionary ID as well as the dictionary property name. Dictionary properties are simply key-value pairs that can be accessed from the {#dictionaries} root-level object property.

Example

Get the value of the dictionary property dict-key defined in dictionary my-dictionary-id using {#dictionaries['my-dictionary-id']['dict-key']}. {% endtab %}

{% tab title="Endpoints" %} When you define endpoints for your API, you need to give them a name that is a unique identifier across all endpoints of the API. This identifier can be used to get an endpoint reference (i.e., a URI) from the {#endpoints} root-level object property.

Example

When you create an API, a default endpoint is created that corresponds to the value you set for the backend property. This endpoint can be retrieved with EL by using the following syntax: {#endpoints['default']}. {% endtab %} {% endtabs %}

Request

EL can be used to access request properties and attributes as described below.

Request object properties

The object properties you can access from the {#request} root-level object property and use for API requests are listed below.

{% tabs %} {% tab title="Table" %}

Object PropertyDescriptionTypeExample
contentBody contentstring-
contextPathContext pathstring/v2/
headersHeaderskey / valueX-Custom → myvalue
hostThe host of the request. This is preferable to using the Host header of the request because HTTP2 requests do not provide this header.stringgravitee.example.com
idIdentifierstring12345678-90ab-cdef-1234-567890ab
localAddressLocal addressstring0:0:0:0:0:0:0:1
methodHTTP methodstringGET
paramsQuery parameterskey / valueorder → 100
pathPathstring/v2/store/MyStore
pathInfoPath infostring/store/MyStore
pathInfosPath info partsarray of strings[,store,MyStore]
pathParamsPath parameterskey / valuestoreId → MyStore (see Warning for details)
pathParamsRawPath parametersstring/something/:id/**
pathsPath partsarray of strings[,v2,store,MyStore]
remoteAddressRemote addressstring0:0:0:0:0:0:0:1
schemeThe scheme of the request (either http or https)stringhttp
hoststring
sslSSL session informationSSL object-
timestampTimestamplong1602781000267
transactionIdTransaction identifierstringcd123456-7890-abcd-ef12-34567890
uriURIstring/v2/store/MyStore?order=100
versionHTTP versionstringHTTP_1_1
{% endtab %}

{% tab title="Examples" %}

  • Get the value of the Content-Type header for an incoming HTTP request using {#request.headers['content-type']}
  • Get the second part of the request path using {#request.paths[1]} {% endtab %} {% endtabs %}

Request context attributes

When APIM Gateway handles an incoming API request, some object properties are automatically created or added during the execution phase through the Assign Attributes policy. These object properties are known as attributes. Attributes can be accessed from the {#context.attributes} root-level object property.

Some policies (e.g., the OAuth2 policy) register other attributes in the request context. For more information, refer to the documentation for individual policies.

Request context attributes and examples are listed below.

{% tabs %} {% tab title="Table" %}

Object PropertyDescriptionTypeNullable
apiCalled APIstring-
api-keyThe API key used (for an API Key plan)stringX (for no API Key plan)
applicationThe authenticated application making incoming HTTP requestsstringX (for Keyless plan)
context-pathContext pathstring-
planPlan used to manage incoming HTTP requestsstring-
resolved-pathThe path defined in policiesstring-
user-id

The user identifier of an incoming HTTP request:

* The subscription ID for an API Key plan

* The remote IP for a Keyless plan

string-
{% endtab %}

{% tab title="Examples" %}

  • Get the value of the user-id attribute for an incoming HTTP request using {#context.attributes['user-id']}
  • Get the value of the plan attribute for an incoming HTTP request using {#context.attributes['plan']} {% endtab %} {% endtabs %}

SSL object properties

The object properties you can access in the ssl session object from the {#request.ssl} root-level object property are listed below.

{% tabs %} {% tab title="Table" %}

Object PropertyDescriptionTypeExample
clientHostHost name of the clientstringclient.domain.com
clientPortPort number of the clientlong443
clientClient informationPrincipal object-
serverServer informationPrincipal object-
{% endtab %}

{% tab title="Example" %} Get the client HOST from the SSL session using {#request.ssl.clientHost} {% endtab %} {% endtabs %}

Principal objects

The client and server objects are of type Principal. A Principal object represents the currently authenticated user who is making the request to the API and provides access to various user attributes such as username, email address, roles, and permissions.

The Principal object is typically used with security policies such as OAuth2, JWT, or basic authentication to enforce access control and authorization rules on incoming requests. For example, a policy can check if the current user has a specific role or permission before allowing them to access a protected resource.

If the Principal object is not defined, client and server object values are empty. Otherwise, there are domain name attributes you can access from the {#request.ssl.client} and {#request.ssl.server} Prinicipal objects as shown in the table below:

{% hint style="warning" %} Limitation on arrays

All attributes of the Principalobject are flattened to be accessed directly with dot or bracket notation. While some of these attributes can be arrays, EL will only return the first item in the array. To retrieve all values of an attribute, use the attributes object property shown in the table and examples below. {% endhint %}

{% tabs %} {% tab title="Table" %}

Object PropertyDescriptionTypeExample
attributesRetrieves all the Prinicipal object's domain name attributes key / value"ou" → ["Test team", "Dev team"]
businessCategoryBusiness categorystring-
cCountry codestringFR
cnCommon namestring-
countryOfCitizenshipRFC 3039 CountryOfCitizenshipstring-
countryOfResidenceRFC 3039 CountryOfResidencestring-
dateOfBirthRFC 3039 RFC 3039 DateOfBirthstring19830719000000Z
dcDomain componentstring-
definedReturns true if the Principal object is defined and contains values. Returns false otherwise.boolean-
descriptionDescriptionstring-
dmdNameRFC 2256 directory management domainstring-
dnFully qualified domain namestring-
dnQualifierDomain name qualifierstring-
eEmail address in Verisign certificatesstring-
emailAddressEmail address (RSA PKCS#9 extension)string-
genderRFC 3039 Genderstring"M", "F", "m" or "f"
generationNaming attributes of type X520namestring-
givennameNaming attributes of type X520namestring-
initialsNaming attributes of type X520namestring-
lLocality namestring-
nameNamestring-
nameAtBirthISIS-MTT NameAtBirthstring-
oOrganizationstring-
organizationIdentifierOrganization identifierstring-
ouOrganization unit namestring-
placeOfBirthRFC 3039 PlaceOfBirthstring-
postalAddressRFC 3039 PostalAddressstring-
postalCodePostal codestring-
pseudonymRFC 3039 Pseudonymstring-
roleRolestring-
serialnumberDevice serial number namestring-
stState or province namestring-
streetStreetstring-
surnameNaming attributes of type X520namestring-
tTitlestring-
telephoneNumberTelephone numberstring-
uidLDAP User idstring-
uniqueIdentifierNaming attributes of type X520namestring-
unstructuredAddressUnstructured address (from PKCS#9)string-
{% endtab %}

{% tab title="Examples" %}

Standard Object Properties

  • Get the client DN from the SSL session: {#request.ssl.client.dn}
  • Get the server organization from the SSL session: {#request.ssl.server.o}

Arrays and boolean logic

  • Get all the organization units of the server from the SSL session:
    • {#request.ssl.server.attributes['ou'][0]}
    • {#request.ssl.server.attributes['OU'][1]}
    • {#request.ssl.server.attributes['Ou'][2]}
  • Get a custom attribute of the client from the SSL session: {#request.ssl.client.attributes['1.2.3.4'][0]}
  • Determine if the SSL attributes of the client are set: {#request.ssl.client.defined} {% endtab %} {% endtabs %}

Response

The object properties you can access for API responses from the {#response} root-level object property are listed below.

{% tabs %} {% tab title="Table" %}

Object PropertyDescriptionTypeExample
contentBody contentstring-
headersHeaderskey / valueX-Custom → myvalue
statusStatus of the HTTP responseint200
{% endtab %}

{% tab title="Example" %} Get the status of an HTTP response: {#response.status} {% endtab %} {% endtabs %}

Message

The object properties you can access for API messages from the {#message} root-level object property are listed below. A message (either sent or received) may also contain attributes that can be retrieved via {#message.attributes[key]}.

{% hint style="info" %} The EL used for a message does not change based on phase. EL is executed on the message itself, so whether the message is sent in the subscribe or publish phase is irrelevant. {% endhint %}

{% tabs %} {% tab title="Table" %}

Object PropertyDescriptionTypeExample
attributeNamesThe names of the attributeslist / array-
attributesAttributes attached to the messagekey / value-
contentContent of the messagestring-
contentLengthSize of the contentinteger-
errorFlag regarding the error state of the messageboolean-
headersHeaders attached to the messagekey / value-
idID of the messagestring-
metadataMetadata attached to the messagekey / value-
{% endtab %}

{% tab title="Examples" %}

  • Get the value of the Content-Type header for a message using {#message.headers['content-type']}
  • Get the size of a message using {#message.contentLength} {% endtab %} {% endtabs %}

Nodes

A node is a component that represents an instance of the Gravitee Gateway. Each node runs a copy of the Gateway that is responsible for handling incoming requests, executing policies, and forwarding requests to the appropriate upstream services. The object properties you can access for nodes from the {#node} root-level object property are listed below.

{% tabs %} {% tab title="Table" %}

Object PropertyDescriptionTypeExample
idNode IDstring975de338-90ff-41ab-9de3-3890ff41ab62
shardingTagsNode sharding tagarray of string[internal,external]
tenantNode tenantstringEurope
versionNode versionstring3.14.0
zoneZone the node is grouped instringeurope-west-2
{% endtab %}

{% tab title="Example" %} Get the version of a node : {#node.version} {% endtab %} {% endtabs %}

Mixin

In previous examples, we showed various ways to manipulate objects available in the EL context. You can also mix root-level object property usage to provide an increasingly dynamic configuration.

For example, to retrieve the value of an HTTP header where the name is based on an API custom property named my-property, use {#request.headers[#api.properties['my-property']]}.

Policies

You can use the EL to update some aspects of policy configuration. The policy specifies if it supports EL or not by including a Condition section in the Policy Studio configuration.

Assign attributes policy supports EL conditions

Conditions

You can use the EL to set a condition of execution (see 'conditional policies and flows conditions') and it is possible to use logical operators such as && or ||, as shown in the example below:

{#request.headers['my-header'] != null && #request.headers['my-header'][0] =="my-value"}

{% hint style="info" %} Alternate equality check

You can use the equals() method instead of ==. When you use .equals(), it is recommended to put the string first to prevent an error. For example, if #request.headers['my-header'] is null , then 'my-value'.equals(#request.headers['my-header'])will prevent an error. {% endhint %}

Debugging

In case of an error when using EL, an exception will be raised :

The template evaluation returns an error. Expression: {#context.error}

If debugging your expression is difficult, consider the following example for guidance:

Assume {#request.content.length() >= 10} is the conditional expression on a flow. When testing, you are expecting the condition to evaluate to false and stop the flow from executing, but the flow continues to function unexpectedly. To check the actual output of the #request.content.length() expression, use the Assign Attributes policy as shown in the arcade below.

{% @arcade/embed flowId="Q5mHqjjdv2gzuuVwLffu" url="https://app.arcade.software/share/Q5mHqjjdv2gzuuVwLffu" %}