Skip to content

Commit

Permalink
Add SEL expression language module to Maestro
Browse files Browse the repository at this point in the history
  • Loading branch information
jun-he committed Apr 20, 2024
1 parent e4c5e1e commit 8eacd5c
Show file tree
Hide file tree
Showing 132 changed files with 18,412 additions and 0 deletions.
24 changes: 24 additions & 0 deletions netflix-sel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Welcome to SEL

## What is SEL
SEL is an expression language (EL) which evaluates Maestro parameters to create dynamic workflow execution.

It is simple, secure, and safe.

* It is a simple EL and the grammar and syntax follow JLS ([Java Language Specifications](https://docs.oracle.com/javase/specs/)).
SEL only supports a subset of JLS as it is designed for scheduler use cases.
For example, it does not support defining a class within a parameter expression.
* It supports permission control built upon Java security features.
* It supports runtime checks (e.g. loop iteration limit, array size check, etc.)
* It does rigorous validations (e.g. throwing a validation error for expression `int x = "hello world"`).


## SEL Language guide

* [Getting started](lang-guide/getting-started.md)
* [Data types](lang-guide/data-type.md)
* [Operators](lang-guide/operator.md)
* [Expressions](lang-guide/expression.md)
* [Statements](lang-guide/statement.md)
* [Supported classes](lang-guide/class-function.md)
* [Examples](lang-guide/example.md)
9 changes: 9 additions & 0 deletions netflix-sel/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apply plugin: 'java-library'

dependencies {
implementation jodaTimeDep
api slf4jApiDep

testImplementation junitDep
testRuntimeOnly junitEngineDep
}
24 changes: 24 additions & 0 deletions netflix-sel/docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Welcome to SEL

## What is SEL
SEL is an expression language (EL) which evaluates Maestro parameters to create dynamic workflow execution.

It is simple, secure, and safe.

* It is a simple EL and the grammar and syntax follow JLS ([Java Language Specifications](https://docs.oracle.com/javase/specs/)).
SEL only supports a subset of JLS as it is designed for scheduler use cases.
For example, it does not support defining a class within a parameter expression.
* It supports permission control built upon Java security features.
* It supports runtime checks (e.g. loop iteration limit, array size check, etc.)
* It does rigorous validations (e.g. throwing a validation error for expression `int x = "hello world"`).


## SEL Language guide

* [Getting started](lang-guide/getting-started.md)
* [Data types](lang-guide/data-type.md)
* [Operators](lang-guide/operator.md)
* [Expressions](lang-guide/expression.md)
* [Statements](lang-guide/statement.md)
* [Supported classes](lang-guide/class-function.md)
* [Examples](lang-guide/example.md)
162 changes: 162 additions & 0 deletions netflix-sel/docs/lang-guide/class-function.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# SEL Classes
In addition to the data type classes, SEL also supports the following classes.
Note that SEL currently supports a limited set of Java class methods used by the existing workflows.
If you have any additional feature requests, please let us know.

## Math class
* `int/long min(int/long, int/long)`
```sel
return Math.min(1, 2); // return 1;
```

* `int/long max(int/long, int/long)`
```sel
return Math.max(1, 2); // return 2;
```

* `double random()`
```sel
return Math.random(); // return a random double between 0.0 and 1.0.
```

* `double pow(int/long/double, int/long/double)`
```sel
return Math.pow(2, 3); // returns the double value of the first argument raised to the power of the second argument
```

## UUID class
* `UUID randomUUID()`
```sel
return UUID.randomUUID().toString(); // returns a random UUID String
```

## Joda DateTime class
* Constructors `new DateTime()`, `new DateTime(long)`,
* `String toString()`
* `DateTime parse(String, DateTimeFormatter)`
* `DateTime withZone(DateTimeZone)`
* `DateTime minusYears(int)`
* `DateTime plusYears(int)`
* `DateTime minusMonths(int)`
* `DateTime plusMonths(int)`
* `DateTime minusWeeks(int)`
* `DateTime plusWeeks(int)`
* `DateTime minusDays(int)`
* `DateTime plusDays(int)`
* `DateTime minusHours(int)`
* `DateTime plusHours(int)`
* `DateTime minusMinutes(int)`
* `DateTime plusMinutes(int)`
* `DateTime minusSeconds(int)`
* `DateTime plusSeconds(int)`
* `DateTime minusMillis(int)`
* `DateTime plusMillis(int)`
* `Boolean isAfter(DateTime)`
* `Boolean isBefore(DateTime)`
* `Boolean isEqual(DateTime)`
* `Property monthOfYear()`
* `Property weekyear()`
* `Property weekOfWeekyear()`
* `Property dayOfYear()`
* `Property dayOfMonth()`
* `Property dayOfWeek()`
* `Property hourOfDay()`
* `Property minuteOfDay()`
* `Property minuteOfHour()`
* `Property secondOfDay()`
* `Property secondOfMinute()`
* `Property millisOfDay()`
* `Property millisOfSecond()`
* `withTimeAtStartOfDay()`
* `DateTime withYear(int)`
* `DateTime withWeekyear(int)`
* `DateTime withMonthOfYear(int)`
* `DateTime withWeekOfWeekyear(int)`
* `DateTime withDayOfYear(int)`
* `DateTime withDayOfMonth(int)`
* `DateTime withDayOfWeek(int)`
* `DateTime withHourOfDay(int)`
* `DateTime withMinuteOfHour(int)`
* `DateTime withSecondOfMinute(int)`
* `DateTime withMillisOfSecond(int)`
* `DateTime withMillisOfDay(int)`
* `long getMillis()`
* `int getYear()`
* `int getHourOfDay()`
* `int getWeekOfWeekyear()`
* `int getWeekyear()`
* `int getDayOfWeek()`
* `int getDayOfMonth()`
* `int getDayOfYear()`
* `int getMillisOfDay()`
* `int getMillisOfSecond()`
* `int getMinuteOfDay()`
* `int getMinuteOfHour()`
* `int getSecondOfMinute()`
* `int getMonthOfYear()`
* `int getSecondOfDay()`
* `DateTime toDateTime(DateTimeZone)`


## Joda DateTimeFormatter class
* `DateTimeFormatter withZone(DateTimeZone)`
* `DateTime parseDateTime(String/int/long)`
* `long parseMillis(String)`
* `DateTimeFormatter forPattern(String) `
* `String print(DateTime/long)`

## Joda DateTimeZone class
* `DateTimeZone forID(String)`
```sel
return DateTimeZone.forID('UTC'); // returns UTC DateTimeZone
```

* `int getOffset(DateTime)`
```sel
return DateTimeZone.UTC.getOffset(new DateTime()); // return the millisecond offset to add to UTC to get local time.
```

* field `UTC`
```sel
return DateTimeZone.UTC; // returns UTC DateTimeZone
```

## Joda DateTimeDays class
* `Days daysBetween(DateTime, DateTime)`
```sel
return Days.daysBetween(dt1, dt2); // returns the number of days between two Joda DateTime objects
```

* `int getDays()`
```sel
return days.getDays(); // returns the number of days that this object represents.
```

## Util class
* `long dateIntToTs(String/int/long)`

* `long dateIntHourToTs(String/int/long, String/int/long, String, String/int/long, String/int/long)`
```sel
// 1st arg is dateInt, 2nd arg is hour, 3rd arg is timezone, 4th arg is day offset, 5th arg is hour offsett.
return Util.dateIntHourToTs("20210707", "01", "UTC", 0, 0); // returns LONG: 1625619600000.
```

* `String incrementDateInt(String/int/long, int)`

* `String tsToDateInt(String/int/long)`

* `String timeoutForDateTimeDeadline(DateTime, String)`

* `String timeoutForDateIntDeadline(String/int/long, String)`

* `long[] dateIntsBetween(String/int/long. String/int/long, String/int/long)`
```sel
return Util.dateIntsBetween(20200226, 20200303, 1); // returns LONG_ARRAY: [20200226, 20200227, 20200228, 20200229, 20200301, 20200302].
```

* `long[] intsBetween(String/int/long. String/int/long, String/int/long)`

## Other classes & methods
* `long System.currentTimeMillis()`

* `DateTimeConstants.SUNDAY`
127 changes: 127 additions & 0 deletions netflix-sel/docs/lang-guide/data-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# SEL Data Types

## Literals
SEL supports three types of literals

* Integer literal
```sel
123;
```

* Double literal
```sel
12.3;
```

* String literal
```sel
"hello world"; // double quoted
'hello world'; // single quoted, define a string literal without escaping)
```

## Date types
SEL supports the same data types as Maestro, including

* String
```sel
String s = "hello world"; // defining a string variable with type info
s = "hello " + "world"; // defining a string variable without type info
```

* Long
```sel
long l = 123; // defining a long variable with type info
Long l = 123; // defining a long variable with type info
l = 12 - 3; // defining a long variable without type info
```

* Double
```sel
double d = 12.3; // defining a double variable with type info
Double d = 12.3; // defining a double variable with type info
d = 1 + 2.3; // defining a double variable without type info
```

* Boolean
```sel
boolean b = true; // defining a boolean variable with type info
Boolean b = false; // defining a boolean variable with type info
b = 2 > 1; // defining a boolean variable without type info
```

* Map<String, Object>, Object is one of types SEL supports.
```sel
Map m = new Map(); // defining a Map<String, Object> variable with type info
Map m = new HashMap(); // defining a Map<String, Object> variable with type info
m = new Map(); // defining a Map<String, Object> variable without type info
m = new HashMap(); // defining a Map<String, Object> variable without type info
m.put('foo', 'bar'); // adding an <key, value> pair into the Map variable.
```

* String Array
```sel
String[] sa = new String[]{'hello', 'world'}; // defining a string array variable with type info
sa = 'hello world'.split(' '); // defining a string array variable with type info
```

* Long Array
```sel
long[] l = new long[]{1, 2, 3}; // defining a long array variable with type info
Long[] l = new Long[]{1, 2, 3}; // defining a long array variable with type info
l = new long[]{1, 2, 3}; // defining a long array variable without type info
l = new Long[]{1, 2, 3}; // defining a long array variable without type info
```

* Double Array
```sel
double[] d = new double[]{1.2, 3.0}; // defining a double array variable with type info
Double[] d = new double[]{1.2, 3.0}; // defining a double array variable with type info
d = new double[]{1.2, 3.0}; // defining a double array variable without type info
d = new Double[]{1.2, 3.0}; // defining a double array variable without type info
```

* Boolean Array
```sel
boolean[] b = new boolean[]{true, false}; // defining a boolean array variable with type info
Boolean[] b = new Boolean[]{true, false}; // defining a boolean array variable with type info
b = new boolean[]{2 > 1, 2 < 1}; // defining a boolean array variable without type info
b = new Boolean[]{2 > 1, 2 < 1}; // defining a boolean array variable without type info
```

To make it easier for users, it also supports

* int/Integer
```sel
int l = 123; // defining a int variable with type info
Integer l = 123; // defining a int variable with type info
l = 12 - 3; // defining a int variable without type info
```

* Float
```sel
float d = 12.3f; // defining a float variable with type info
Float d = 12.3f; // defining a float variable with type info
d = 1.0f + 2.3f; // defining a float variable without type info
```

* int/Integer array
```sel
int[] l = new int[]{1, 2, 3}; // defining a long array variable with type info
Integer[] l = new Integer[]{1, 2, 3}; // defining a long array variable with type info
l = new int[]{1, 2, 3}; // defining a long array variable without type info
l = new Integer[]{1, 2, 3}; // defining a long array variable without type info
```

* Float array
```sel
float[] d = new float[]{1.2f, 3.0f}; // defining a double array variable with type info
Float[] d = new Float[]{1.2f, 3.0f}; // defining a double array variable with type info
d = new float[]{1.2f, 3.0f}; // defining a double array variable without type info
d = new Float[]{1.2f, 3.0f}; // defining a double array variable without type info
```

## Notes
* SEL only supports 1 dimensional array
* Within the expression, all accessible Maestro parameter values can be obtained by referencing parameter name itself,
e.g. `foo`, or by using a special Map object, e.g. `params['foo']`.
Loading

0 comments on commit 8eacd5c

Please sign in to comment.