Scripted Fields for Billing Meters
What Are Scripted Fields?
If a field does not exist in your request or response that you’d like to bill upon, you can create the field using a script. The script retroactively creates a custom field from other fields, using formulas, arithmetic, and conditional expressions. A scripted field has the follow capabilities and limitations:
- Only numbers, booleans, and datetime data types (treated as a number, milliseconds since epoch) are supported.
[field.path.field_name]
references a field.- If field value does not exist, for numbers it defaults to 0, and for dates it defaults to the epoch. You can use
[field.path.field_name|50]
to set the default value. - Most mathematical functions are supported. Conditional expressions using
if
,else
,then
, andend
operators are supported. - Only expressions are supported. No other variables or statements are supported. Scripted fields with body fields may take several minutes to query.
Creating a Scripted Field
To add a scripted field, follow these steps:
- Select Event Count and then select { } Select Field.
- Select <> Scripted Field from the Select a field list.
-
Write the script and then select Set. The field list following the script text box contains all the existing fields so that you can easily reference them in your script. For more information on how to write scripts, see Using the Scripting Language.
Using the Scripting Language
When using Scripted Fields, you create the new field by using MoesifFieldScript. MoesifFieldScript, also referred to as MSF, is a domain-specific language designed to perform custom computations on field values from events within the Moesif platform. This language provides the ability to evaluate logical, comparison, arithmetic, and functional expressions on event fields. Additionally, it supports accessing specific keys from the event data and provides default values if the specified key is not found.
Supported Constants, Operators, and Functions
MoesifFieldScript supports a variety of different mathematical constants, operators, and functions that can be used to create a value for a custom field. Below is list of currently supported entities.
Constants
type | values |
---|---|
Numeric Constants | Any whole number or decimal like 123 , 4.56 |
Boolean Constants | true or false |
Operators
Arithmetic Operators:
symbol | operator name/function |
---|---|
+ |
Addition |
- |
Subtraction |
* |
Multiplication |
/ |
Division |
Comparison Operators:
symbol | operator name/function |
---|---|
== |
Equals |
!= |
Not Equals |
< |
Less Than |
<= |
Less Than or Equal |
> |
Greater Than |
>= |
Greater Than or Equal |
Logical Operators:
symbol | operator name/function |
---|---|
and |
Logical AND |
or |
Logical OR |
Functions
Numeric Functions
symbol | function name/functionality |
---|---|
round |
Rounds a number |
ceil |
Rounds a number up |
floor |
Rounds a number down |
abs |
Absolute value of a number |
min |
Minimum of numbers |
max |
Maximum of numbers |
avg |
Average of numbers |
exp |
Exponential function |
log |
Natural logarithm |
pow |
Power of a number |
sqrt |
Square root |
String Functions
symbol | function name/functionality |
---|---|
countSubstrings |
Count of substrings in a string |
Examples
Below are some examples of how these operators and functions can be used within a MSF expression.
-
To check if the value in a field, such as
age
, is less than 18:[age] < 18
-
To sum the value of two fields, such as
price
andtax
:[price] + [tax]
-
To get the maximum of two fields, such as
score1
andscore2
:max([score1], [score2])
-
As a conditional expression, such as to check if
status
isactive
and return1
, otherwise0
:if [status] == "active" then 1 else 0 end
Referencing One Or More Fields in The Event
In Moesif, every API event is structured as an event document with various fields, including details about the request, response, and other associated metadata. When writing expressions in MoesifFieldScript, you can access these fields using a key path. This key path is a flattened representation of the JSON structure that omits array indices and focuses only on the names of JSON keys.
The schema of the event document is the same as what is provided in the event collector API. For more information on the schema, check out the API Call docs
To access specific values from the event data in MoesifFieldScript, you can use the following ways to access the value
within a specific field using the fields key
:
[keyName]
: Accesses the value of the keykeyName
.[key1.key2]
: Accesses nested keys.[keyName|defaultValue]
: Accesses the keykeyName
and if not found, usesdefaultValue
.
Note: Special characters in key names should be escaped with a backslash (e.g. \\[
or \\\\
).
Accessing Top-Level Fields
In Moesif, certain fields at the top level (those not contained in the request or response themselves), can also be accessed. For top-level fields in the event schema, such as time
, user_id
, and direction
, MoesifFieldScript can access them directly by using their respective names. Below are two examples of such fields:
[time]
: Accesses the timestamp of the request.[user_id]
: Accesses the associated user of the API call.
Accessing Request and Response Body Fields
Depending on how Moesif is configured, you may be able to use request and response body fields within your Scripted Field. For fields inside the body
of the request
and response
objects, you can use the flattened JSON key path to access them. For example, consider an API event with the following request body:
{
"request": {
"body": {
"fields": {
"name": "John",
"address": {
"city": "San Francisco",
"state": "CA"
}
}
}
}
}
To access the name
field in MoesifFieldScript, you could use the following:
[request.body.fields.name]
To access the city
inside the address
object, you could use the following:
[request.body.fields.address.city]
Accessing Fields Within an Array
If there are arrays within the JSON structure, the flattened key path omits array indices. Instead, it focuses on accessing the nested keys directly. This simplifies access to fields and ensures consistency in expression writing. For example, Consider an API event with this request body:
{
"request": {
"body": {
"fields": {
"users": [
{"id": 1, "name": "John"},
{"id": 2, "name": "Jane"}
]
}
}
}
}
To access the name
of the second user, you would not use an array index. Instead, you’d access it as:
[request.body.fields.users.name]
It’s important to note that without array indices, this method can only access fields when the structure is known and consistent. If you need to access specific array elements, additional methods or processing might be required outside of MoesifFieldScript
Working With Dates
In MoesifFieldScript, date values are represented as the number of milliseconds that have elapsed since the Unix epoch (January 1, 1970, 00:00:00 GMT). This numeric representation allows for easy date comparison and arithmetic operations. Below are some examples of how dates can be used within a Scripted Field.
Checking If a Date is Before a Specific Time
To check if the date in the field fields.example.date
is before January 2, 1970, 00:00:01 GMT (the Unix Epoch) or the value 1000
, the timestamp for this specific time in milliseconds since epoch is 1000
, the expression would be:
[fields.example.date] < 1000
Calculating The Difference Between Two Date Fields
If you have two date fields you’d like to find the difference between, you can simply use an equation in the expression to do so. For example, to find the difference in milliseconds between startDate
and endDate
fields, the expression would be:
[endDate] - [startDate]
Checking If an Event Occurred Within a Given Time Period
To return a boolean value of whether an event occurred within a given time period, you can calculate the difference between the current time and the event date and see if it is less than the time period you are concerned with. For example, given that there are 86,400,000 milliseconds in a day, to check if an event with the field eventDate
occurred in the last 24 hours you could use the following expression:
[currentTime] - [eventDate] <= 86400000
Where currentTime
is the current timestamp.
Adding Days/Time to a Date:
It is also possible to add days and time to a given date. For example, to add 7 days to a date value in the eventDate
field, you can simply add 604800000
(the amount of milliseconds in one week) to the current value. That expression would lok like so:
[eventDate] + 604800000
Rounding Down to The Nearest Day:
If you want to ignore the time component and just consider the date, you can use the following expression to divide the timestamp by the number of milliseconds in a day, round down to get the number of days since the epoch, and then multiply the result by the number of milliseconds in a day to get the timestamp for the start of the day.
floor([eventDate] / 86400000) * 86400000
Working With Strings
Counting Substrings in a String:
If you want to count how many times a substring occurs in a string field, you can use countSubstrings()
.
countSubstrings([response.body.name|""], "AVG")
Support for Conditionals
In the scripted field, you will be able to add in conditionals, such as an if-else
statement. Below are a few examples of how conditionals can be used
As a traditional, multi-line if-else
statement:
if [request.query.billable] != true
then
1
else
2 * [response.body.usage]
end
or in a single-line statement:
if [field.value] == 42 then 2.2 * 31 else if [field.value] > 42 then 1.7 * 31 else 1.2 * 31 end end
Boolean operator’s can also be used within the conditional statement:
if [field.value] < 42 and [field.value] != 0 then 1 + 2 else 2 * 3 end
Another example is as part of a mathematical function, such as log
:
log(if [request.body.pi] > 3 then
[field.value]
else
1.23
end)
Default Values
If a key being queried in an expression is not found, a default value can be provided. To specify a default value you can use the following syntax:
[user.age|25]
In this example, if user.age
is not defined, the value used will be 25
.