Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Playground function library #73

Merged
merged 21 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ Powerful formula-syntax evaluator for Apex and LWC.

### Unlocked Package (`expression` namespace)

[![Install Unlocked Package in a Sandbox](assets/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04tDm0000011MhfIAE)
[![Install Unlocked Package in Production](assets/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04tDm0000011MhfIAE)
[![Install Unlocked Package in a Sandbox](assets/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04tDm0000011MhkIAE)
[![Install Unlocked Package in Production](assets/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04tDm0000011MhkIAE)

Install with SF CLI:

```shell
sf package install --apex-compile package --wait 20 --package 04tDm0000011MhfIAE
sf package install --apex-compile package --wait 20 --package 04tDm0000011MhkIAE
```

Install with SFDX CLI:

```shell
sfdx force:package:install --apexcompile package --wait 20 --package 04tDm0000011MhfIAE
sfdx force:package:install --apexcompile package --wait 20 --package 04tDm0000011MhkIAE
```

### Direct Deployment to Salesforce
Expand Down Expand Up @@ -246,7 +246,7 @@ expressionEvaluator.run(expression); // "World World!"
A special function, `FETCH`, is provided which allows you to query data from the database. This is useful
when the data you want to use is not provided as part of the context.

The `FETCH` function takes 2 arguments: a string with the `SOjectName` you wish to extract data from,
The `FETCH` function takes 2 arguments: a string with the `SObjectName` you wish to extract data from,
and a list of strings with the fields you wish to extract. This will query all the records of the given
type and return a list of `SObjects` with the data.

Expand All @@ -263,7 +263,7 @@ Object result = expression.Evaluator.run('MAP(WHERE(FETCH("Account", ["Id", "Nam
Note that when using this function, the automatic context resolution is not performed, so you need to
explicitly specify all fields you wish to reference in the formula.

At this moment advanced querying capabilities like filtering, sorting, or limiting the number of records
At this moment, advanced querying capabilities like filtering, sorting, or limiting the number of records
are not supported. To get over these limitations, you can create a custom formula using Apex. See the
[Advanced Usage](#advanced-usage) section for more information.

Expand Down Expand Up @@ -665,7 +665,7 @@ Returns TRUE if a text field contains a given string.
Accepts 2 arguments: the text field and the string to match.

```apex
expression.Evaluator.run('CONTAINS("Hello World", "llo Wo")'); // true
expression.Evaluator.run('CONTAINS("Hello World", "llo Wo")'); // true
```

- `LOWER`
Expand Down Expand Up @@ -903,7 +903,7 @@ Returns a date value from the provided year, month, and day values.
Accepts 3 arguments: the year, month, and day.

```apex
expression.Evaluator.run('DATE(2020, 1, 1)'); // 2020-01-01 00:00:00
expression.Evaluator.run('DATE(2020, 1, 1)'); // 2020-01-01
```

- `ADDMONTHS`
Expand All @@ -913,7 +913,7 @@ Returns a date that is a specified number of months before or after a given date
Accepts 2 arguments: the date and the number of months to add.

```apex
expression.Evaluator.run('ADDMONTHS(DATE(2020, 1, 1), 1)'); // 2020-02-01 00:00:00
expression.Evaluator.run('ADDMONTHS(DATE(2020, 1, 1), 1)'); // 2020-02-01
```

- `DAY`
Expand Down Expand Up @@ -954,8 +954,8 @@ a datetime.
Accepts 1 argument: the date as a string or datetime.

```apex
expression.Evaluator.run('DATEVALUE("2020-01-01")'); // 2020-01-01 00:00:00
expression.Evaluator.run('DATEVALUE(NOW())'); // 2020-01-01 00:00:00
expression.Evaluator.run('DATEVALUE("2020-01-01")'); // 2020-01-01
expression.Evaluator.run('DATEVALUE(NOW())'); // 2020-01-01
```

- `DATETIMEVALUE`
Expand Down Expand Up @@ -1069,24 +1069,24 @@ Accepts 1 argument: the time to evaluate.
expression.Evaluator.run('MILLISECOND(TIMEVALUE("12:00:00.123"))'); // 123
```

- `MINUTE`
- `SECOND`

Returns the minute value of a provided time.
Returns the second value of a provided time.

Accepts 1 argument: the time to evaluate.

```apex
expression.Evaluator.run('MINUTE(TIMEVALUE("12:10:00"))'); // 10
expression.Evaluator.run('SECOND(TIMEVALUE("12:00:45"))'); //45
```

- `SECOND`
- `MINUTE`

Returns the second value of a provided time.
Returns the minute value of a provided time.

Accepts 1 argument: the time to evaluate.

```apex
expression.Evaluator.run('SECOND(TIMEVALUE("12:00:45"))'); //45
expression.Evaluator.run('MINUTE(TIMEVALUE("12:10:00"))'); // 10
```

- `HOUR`
Expand Down Expand Up @@ -1121,7 +1121,7 @@ Returns the day of the week for a given date.
Accepts 1 argument: the date to evaluate.

```apex
expression.Evaluator.run('WEEKDAY(DATE(2020, 1, 1))'); // 4
expression.Evaluator.run('WEEKDAY(DATE(2020, 1, 1))'); // 2
```

- `FORMATDURATION`
Expand Down Expand Up @@ -1663,7 +1663,7 @@ expression.Evaluator.run('TRUNC(1.5)'); // 1
expression.Evaluator.run('TRUNC(1.5, 1)'); // 1.5
```

#### Misc Functions
#### Data Functions

- `TRANSFORM`

Expand Down
Binary file modified assets/expression-playground.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 14 additions & 4 deletions expression-src/main/api/Evaluator.cls
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ global with sharing class Evaluator {
*/
global static Object run(String formula, SObject context, Config config) {
Expr pipedExpression = parseExpression(formula, config);
return interpretWithContext(context, pipedExpression);
return interpretWithContext(context, pipedExpression, config);
}

/**
Expand Down Expand Up @@ -76,7 +76,7 @@ global with sharing class Evaluator {
global static Object run(String formula, Id recordId, Config config) {
Expr pipedExpression = parseExpression(formula, config);
SObject record = getRecord(recordId, pipedExpression);
return interpretWithContext(record, pipedExpression);
return interpretWithContext(record, pipedExpression, config);
}

private static Expr parseExpression(String formula, Config config) {
Expand All @@ -102,9 +102,9 @@ global with sharing class Evaluator {
return ctxInterpreter.build(pipedExpression);
}

private static Object interpretWithContext(SObject record, Expr pipedExpression) {
private static Object interpretWithContext(SObject record, Expr pipedExpression, Config config) {
Environment env = new Environment(record);
Interpreter interpreter = new Interpreter(env);
IInterpreter interpreter = getInterpreter(env, config);
return interpreter.interpret(pipedExpression);
}

Expand All @@ -116,5 +116,15 @@ global with sharing class Evaluator {
global class Config {
global SharingMode sharing = SharingMode.WITH;
global Boolean printAst = false;
public Boolean withDiagnostics = false;
}

private static IInterpreter getInterpreter(Environment env, Config config) {
IInterpreter mainInterpreter = new Interpreter(env);
if (config.withDiagnostics) {
return new DiagnosticsDecorator(mainInterpreter);
} else {
return mainInterpreter;
}
}
}
88 changes: 81 additions & 7 deletions expression-src/main/editor/controllers/PlaygroundController.cls
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
public with sharing class PlaygroundController {
@AuraEnabled(Cacheable=true)
public static List<String> getFunctions() {
Set<String> builtInFunctionNames = ExpressionFunction.FUNCTIONS.keySet();
public static List<String> getFunctionNames() {
Set<String> builtInFunctionNames = ExpressionFunction.getStandardFunctionNames();
Set<String> customFunctionNames = Expression_Function__mdt.getAll().keySet();
Set<String> functionNames = new Set<String>();
functionNames.addAll(builtInFunctionNames);
Expand All @@ -11,14 +11,38 @@ public with sharing class PlaygroundController {
);
}

@AuraEnabled(Cacheable=true)
public static List<FunctionCategory> getCustomFunctions() {
List<FunctionCategory> toReturn = new List<FunctionCategory>();

List<Expression_Function__mdt> customFunctions = Expression_Function__mdt.getAll().values();
FunctionCategory customCategory = new FunctionCategory();
customCategory.name = 'Custom';
customCategory.icon = 'utility:apex_plugin';

List<Function> functions = new List<Function>();
for (Expression_Function__mdt customFunctionRecord : customFunctions) {
Function customFunction = new Function();
customFunction.name = customFunctionRecord.DeveloperName;
customFunction.autoCompleteValue = customFunctionRecord.DeveloperName + '(';
functions.add(customFunction);
}
customCategory.functions = functions;
toReturn.add(customCategory);

return toReturn;
}

@AuraEnabled
public static Result validate(String expr, Id recordId) {
Result toReturn = new Result();
Evaluator.Config config = new Evaluator.Config();
config.withDiagnostics = true;
try {
if (recordId != null) {
toReturn.result = Evaluator.run(expr, recordId);
if (String.isNotBlank(recordId)) {
toReturn.result = Evaluator.run(expr, recordId, config);
} else {
toReturn.result = Evaluator.run(expr);
toReturn.result = Evaluator.run(expr, config);
}
} catch (Exceptions.PositionAwareException e) {
EvaluationError error = new EvaluationError();
Expand All @@ -29,14 +53,15 @@ public with sharing class PlaygroundController {
error.startColumnNumber = e.position.columnStart;
error.endColumnNumber = e.position.columnEnd;
toReturn.error = error;
}
catch (Exception e) {
} catch (Exception e) {
EvaluationError error = new EvaluationError();
error.summary = e.getMessage();
error.message = e.getMessage();
toReturn.error = error;
}

toReturn.diagnostics = new Diagnostics(DiagnosticsDecorator.result);

return toReturn;
}

Expand All @@ -46,6 +71,55 @@ public with sharing class PlaygroundController {

@AuraEnabled
public Object result;

@AuraEnabled
public Diagnostics diagnostics;
}

public class Diagnostics {
@AuraEnabled
public Integer cpuTime;

@AuraEnabled
public Integer dmlStatements;

@AuraEnabled
public Integer queries;

@AuraEnabled
public Integer queryRows;

public Diagnostics(DiagnosticsDecorator.DiagnosticsResult result) {
this.cpuTime = result?.cpuTime;
this.dmlStatements = result?.dmlStatements;
this.queries = result?.queries;
this.queryRows = result?.queryRows;
}
}

public class FunctionCategory {
@AuraEnabled
public String name;

@AuraEnabled
public String icon;

@AuraEnabled
public List<Function> functions;
}

public class Function {
@AuraEnabled
public String name;

@AuraEnabled
public String autoCompleteValue;

@AuraEnabled
public String description;

@AuraEnabled
public String example;
}

public class EvaluationError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
private class PlaygroundControllerTest {
@IsTest
static void getsTheExistingFunctionList() {
List<String> functionNames = PlaygroundController.getFunctions();
List<String> functionNames = PlaygroundController.getFunctionNames();
Assert.isFalse(functionNames.isEmpty());
}

@IsTest
static void getCustomFunctions() {
List<PlaygroundController.FunctionCategory> functionNames = PlaygroundController.getCustomFunctions();
Assert.isFalse(functionNames.isEmpty());
}

Expand Down
37 changes: 37 additions & 0 deletions expression-src/main/editor/lwc/functionLibrary/functionLibrary.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
pre {
font-size: 1rem;
background: #eee;
border: 0 solid #3d7e9a;
color: #333;
margin-top: 0;
margin-bottom: 20px;
padding: 15px;
position: relative;
font-family: consolas, monaco, 'Andale Mono', monospace;
font-style: normal;
font-weight: 400;
line-height: 1.5;
overflow: auto;
direction: ltr !important;
text-align: left !important;
border-left-width: 5px !important;
-moz-tab-size: 4;
tab-size: 4;
-moz-hyphens: none;
-webkit-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}

.display-block {
display: block;
}

.pin-doc {
top: 0;
right: 10px;
}
.doc-container {
overflow-y: auto;
max-height: 100%;
}
Loading
Loading