@letsflow/jmespath is a TypeScript implementation of the JMESPath spec with additional functionality for LetsFlow workflows.
JMESPath is a query language for JSON. It takes a JSON document as input and transforms it into another JSON document given a JMESPath expression.
{% hint style="success" %} It's important to know and understand how to use JMESPath expressions to unlock the full potential of LetsFlow. It's highly recommended that you follow the JMESPath Tutorial. {% endhint %}
{% embed url="https://jmespath.org/tutorial.html" %}
This LetsFlow fork of JMESPath extends the original specs, adding the following functionality;
Additionally, it adds the following functions:
- if - Conditional expression
- range - Generate a range of numbers or prefixed strings
- to_object - Convert an array of key-value pairs into an object
- json_serialize - Serialize a JSON value to a string
- json_parse - Parse a JSON string into a JSON object
- sha256 - Calculate the SHA-256 hash of a string
- sha512 - Calculate the SHA-512 hash of a string
- uuid - Generate a UUID
- regex_test - Test if a string matches a regular expression
- regex_match - Return the first match of a regular expression in a string
- regex_match_all - Return all matches of a regular expression in a string
- regex_replace - Replace parts of a string matching a regular expression with a replacement string
yarn add @letsflow/jmespathimport { search } from '@letsflow/jmespath';
search(
{ foo: { bar: { baz: [0, 1, 2, 3, 4] } } },
"foo.bar.baz[2]"
);
// OUTPUTS: 2The LetsFlow fork of JMESPath adds the following functionality, which is not in the original specs.
registerFunction(functionName: string, customFunction: RuntimeFunction, signature: InputSignature[]): void
Extend the list of built-in JMESpath expressions with your own functions.
import {search, registerFunction, TYPE_NUMBER} from '@letsflow/jmespath'
search({ foo: 60, bar: 10 }, 'divide(foo, bar)')
// THROWS ERROR: Error: Unknown function: divide()
registerFunction(
'divide', // FUNCTION NAME
(resolvedArgs) => { // CUSTOM FUNCTION
const [dividend, divisor] = resolvedArgs;
return dividend / divisor;
},
[{ types: [TYPE_NUMBER] }, { types: [TYPE_NUMBER] }] //SIGNATURE
);
search({ foo: 60, bar: 10 }, 'divide(foo, bar)');
// OUTPUTS: 6Optional arguments are supported by setting {..., optional: true} in argument signatures
registerFunction(
'divide',
(resolvedArgs) => {
const [dividend, divisor] = resolvedArgs;
return dividend / divisor ?? 1; //OPTIONAL DIVISOR THAT DEFAULTS TO 1
},
[{ types: [TYPE_NUMBER] }, { types: [TYPE_NUMBER], optional: true }] //SIGNATURE
);
search({ foo: 60, bar: 10 }, 'divide(foo)');
// OUTPUTS: 60Use $ to access the document root.
search({foo: { bar: 999 }, baz: [1, 2, 3]}, '$.baz[*].[@, $.foo.bar]')
// [ [ 1, 999 ], [ 2, 999 ], [ 3, 999 ] ]Numbers in the root scope are treated as number literals. This means that you don't need to quote numbers with backticks.
search([{"bar": 1}, {"bar": 10}], '[?bar==10]')
// [{"bar": 10}]You can also use numbers in arithmetic operations
search({}, '16 + 26'); // 42Syntax:
if(condition, thenValue, elseValue?)Description:
Returns thenValue if condition is true, otherwise returns elseValue. If elseValue is not provided, it defaults to null.
Example:
if(@ > 10, "large", "small")Syntax:
range(start, end, prefix?)Description:
Generates an array of numbers or prefixed strings from start to end - 1. If prefix is provided, each number is prefixed.
Example:
range(5) // [0, 1, 2, 3, 4]
range(1, 5) // [1, 2, 3, 4]
range(1, 5, 'item_') // ["item_1", "item_2", "item_3", "item_4"]Syntax:
to_object(entries)Description:
Converts an array of key-value pairs into an object.
Example:
to_object([['key1', 'value1'], ['key2', 'value2']])
// { "key1": "value1", "key2": "value2" }
[ 'value1', 'value2'] | to_object(zip(range(1, length(@) + 1, 'key'), @))
// { "key1": "value1", "key2": "value2" }Syntax:
json_serialize(value)Uses a deterministic version of JSON.stringify to serialize the value.
Description:
Serializes a JSON value to a string.
Example:
json_serialize({ key: 'value' })
// "{\"key\":\"value\"}"Syntax:
json_parse(string)Description:
Parses a JSON string into a JSON object.
Example:
json_parse("{\"key\":\"value\"}")
// { "key": "value" }Syntax:
sha256(string)Description:
Calculates the SHA-256 hash of a string and returns it as a hexadecimal string.
Example:
sha256('hello')
// "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"Syntax:
sha512(string)Description:
Calculates the SHA-512 hash of a string and returns it as a hexadecimal string.
Example:
sha512('hello')
// "9b71d224bd62f3785d96d46ad3ea3d73319b0c44e59b202205c5d235a0a6caa5a3b36f8c0ab9d45df9215bf07d4d1552c0b1f8bd2671c8a7a3d126f457d79d72"Syntax:
uuid(name, namespace?)Description:
Generates a UUID. If name and (optionally) namespace are provided, generates a version 5 UUID; otherwise, generates a version 4 UUID.
Example:
uuid('example') // v5 UUID
uuid('example', '6ba7b810-9dad-11d1-80b4-00c04fd430c8') // v5 UUID with namespacename must be a string. Use json_serialize() to convert a JSON object to a string.
namespace must be a UUID string. By default, it uses the NIL UUID.
The UUID RFC pre-defines four namespaces
- NameSpace_DNS:
6ba7b810-9dad-11d1-80b4-00c04fd430c8 - NameSpace_URL:
6ba7b811-9dad-11d1-80b4-00c04fd430c8 - NameSpace_OID:
6ba7b812-9dad-11d1-80b4-00c04fd430c8 - NameSpace_X500:
6ba7b814-9dad-11d1-80b4-00c04fd430c8
You might use a UUID for a URL to create a custom namespace. For example; LetsFlow uses the following logic to create the UUID for processes.
uuid(
json_serialize(scenario),
uuid('https://schemas.letsflow.io/v1.0/scenario', '6ba7b811-9dad-11d1-80b4-00c04fd430c8')
) Syntax:
regex_test(regex, string)Description:
Tests if a string matches a given regular expression.
Example:
regex_test('/^hello/', 'hello world') // true
regex_test('/^hello/', 'HELLO world') // false
regex_test('/^hello/i', 'HELLO world') // trueSyntax:
regex_match(regex, string)Description:
Returns the first match of a regular expression in a string as an array.
Example:
regex_match('/hello (\\w+)/', 'hello world')
// ["hello world", "world"]
regex_match('/\\w+/g', 'hello world')
// ["hello", "world"]Syntax:
regex_match_all(regex, string)Description:
Returns all matches of a regular expression in a string as an array of arrays.
Example:
regex_match_all('/(\\w+)=(\d+)/g', 'foo=24 bar=99')
// [["foo=24", "foo", "24"], ["bar=99", "bar", "99"]]Syntax:
regex_replace(regex, replacement, string)Description:
Replaces parts of a string matching a regular expression with a replacement string.
Example:
regex_replace('/world/', 'universe', 'hello world')
// "hello universe"