Skip to content

Commit 1b5d235

Browse files
committed
Ability to provide a custom record context besides the standard record Id.
1 parent 0c91682 commit 1b5d235

File tree

9 files changed

+163
-77
lines changed

9 files changed

+163
-77
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// TODO:Make global
2+
public with sharing class CustomRecordContext {
3+
public final String key;
4+
public final Id recordId;
5+
6+
public CustomRecordContext(String key, Id recordId) {
7+
this.key = key;
8+
this.recordId = recordId;
9+
}
10+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<apiVersion>60.0</apiVersion>
4+
<status>Active</status>
5+
</ApexClass>

expression-src/main/api/Evaluator.cls

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* It provides a set of static methods that allow you to evaluate expressions
44
* in a variety of contexts.
55
*/
6-
// TODO: Add all overloads to the bulk endpoints
76
global with sharing abstract class Evaluator {
87
/**
98
* @description Evaluates an expression and returns the result.
@@ -296,7 +295,7 @@ global with sharing abstract class Evaluator {
296295
* @description Evaluates a formula and returns the result using a record Id as the context.
297296
* When using this endpoint field references will automatically be resolved
298297
* and queried.
299-
* @param formula The formula to evaluate.
298+
* @param expression The formula to evaluate.
300299
* @param recordId The Id of the record to use as the context for the formula.
301300
* @return The result of the formula.
302301
* @example
@@ -306,8 +305,8 @@ global with sharing abstract class Evaluator {
306305
* String recordName = (String)expression.Evaluator.run('Name', record.Id);
307306
* ```
308307
*/
309-
global static Object run(String formula, Id recordId) {
310-
return run(formula, recordId, new Configuration());
308+
global static Object run(String expression, Id recordId) {
309+
return run(expression, recordId, new Configuration());
311310
}
312311

313312
/**
@@ -343,6 +342,10 @@ global with sharing abstract class Evaluator {
343342
return results;
344343
}
345344

345+
public static Object run(String expression, CustomRecordContext context) {
346+
return EvaluatorResolver.forCustomContext(context).evaluate(expression, new Configuration()).resultValue;
347+
}
348+
346349
/**
347350
* @description Evaluates a formula and returns the result using a set of record Ids as the context.
348351
* When using this endpoint field references will automatically be resolved

expression-src/main/src/interpreter/ContextResolver.cls

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
public with sharing class ContextResolver implements Visitor {
22
private Boolean shouldExecuteQuery = false;
33
private Query queryContext;
4+
private String contextReferenceName = '@context';
45

56
private final List<Expr.FunctionDeclaration> customFunctionDeclarations;
67
private final Query topLevelQuery;
78
private final List<Query> subQueries = new List<Query>();
89

9-
public ContextResolver(Id recordId, List<Expr.FunctionDeclaration> customFunctionDeclarations) {
10+
public ContextResolver(Id recordId, List<Expr.FunctionDeclaration> customFunctionDeclarations, String contextReferenceName) {
1011
this.queryContext = new Query(recordId);
1112
this.topLevelQuery = this.queryContext;
1213
this.customFunctionDeclarations = customFunctionDeclarations;
14+
if (String.isNotBlank(contextReferenceName)) {
15+
this.contextReferenceName = contextReferenceName;
16+
}
1317
}
1418

15-
public ContextResolver(Set<Id> recordIds, List<Expr.FunctionDeclaration> customFunctionDeclarations) {
19+
public ContextResolver(Set<Id> recordIds, List<Expr.FunctionDeclaration> customFunctionDeclarations, String contextReferenceName) {
1620
this.queryContext = new Query(recordIds);
1721
this.topLevelQuery = this.queryContext;
1822
this.customFunctionDeclarations = customFunctionDeclarations;
23+
if (String.isNotBlank(contextReferenceName)) {
24+
this.contextReferenceName = contextReferenceName;
25+
}
1926
}
2027

2128
private Object resolve(Expr expr) {
@@ -222,9 +229,9 @@ public with sharing class ContextResolver implements Visitor {
222229
return relationshipToQuery + getExpr.field.lexeme;
223230
}
224231

225-
private static Boolean isReferencingContextField(Expr.GetExpr expr) {
232+
private Boolean isReferencingContextField(Expr.GetExpr expr) {
226233
if (expr.objectExpr instanceof Expr.Variable) {
227-
return ((Expr.Variable)expr.objectExpr).isContext();
234+
return ((Expr.Variable)expr.objectExpr).isContext(this.contextReferenceName);
228235
} else if (expr.objectExpr instanceof Expr.GetExpr) {
229236
// Recursively check if the parent object is referencing a context field
230237
// since we can have nested get expressions and what is important is the top level
@@ -235,7 +242,7 @@ public with sharing class ContextResolver implements Visitor {
235242
}
236243

237244
public Object visit(Expr.Variable variable) {
238-
if (variable.isContext()) {
245+
if (variable.isContext(this.contextReferenceName)) {
239246
// If context is being accessed, then we always want to run the query.
240247
this.shouldExecuteQuery = true;
241248

@@ -483,7 +490,6 @@ public with sharing class ContextResolver implements Visitor {
483490
public Query(Set<Id> recordIds) {
484491
this.recordId = new MultipleIds(recordIds);
485492
this.objectType = this.recordId.getSobjectType();
486-
System.debug('Querying for ' + this.objectType);
487493
this.queryBuilder = new Q(this.objectType);
488494
this.queryBuilder.selectField('Id');
489495
}

expression-src/main/src/interpreter/Environment.cls

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,17 @@ public with sharing class Environment {
7373
return (Expr.FunctionDeclaration)GLOBAL_CONTEXT_VARIABLES.get(functionName.toLowerCase());
7474
}
7575

76+
/**
77+
* @description Adds a global context variable that can be accessed from any formula.
78+
* @param prefix The prefix to be added to the name of the global context variable.
79+
* @param name The name of the global context variable.
80+
* @param value The value to be stored in the global context variable.
81+
*/
82+
public static void addGlobalContextVariable(String prefix, String name, Object value) {
83+
String prefixedName = prefix + name;
84+
GLOBAL_CONTEXT_VARIABLES.put(prefixedName.toLowerCase(), value);
85+
}
86+
7687
/**
7788
* @description Adds a global context variable that can be accessed from any formula.
7889
* Prefixes the name with '@'.

expression-src/main/src/interpreter/Expr.cls

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ public abstract class Expr {
165165
return v.visit(this);
166166
}
167167

168-
public Boolean isContext() {
169-
return this.name.lexeme.toLowerCase() == '@context';
168+
public Boolean isContext(String contextReferenceName) {
169+
return this.name.lexeme.toLowerCase() == contextReferenceName.toLowerCase();
170170
}
171171
}
172172

0 commit comments

Comments
 (0)