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

Refactoring #81

Merged
merged 10 commits into from
Nov 5, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ public with sharing class FormulaEvaluatorUiController {
@AuraEnabled(Cacheable=true)
public static Object evaluate(String recordId, String formula, Boolean respectSharing) {
try {
Evaluator.Config config = new Evaluator.Config();
config.sharing = respectSharing ? Evaluator.SharingMode.WITH : Evaluator.SharingMode.WITHOUT;
Configuration config = new Configuration().respectSharing(respectSharing);
if (String.isBlank(recordId)) {
return Evaluator.run(formula, config);
} else {
Expand Down
43 changes: 43 additions & 0 deletions expression-src/main/api/Configuration.cls
cesarParra marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
global with sharing class Configuration {
public static Configuration.SharingMode evaluationSharingMode;

global enum SharingMode {
WITH,
WITHOUT
}

global SharingMode sharing = SharingMode.WITH;
global Boolean printAst = false;
public Boolean withDiagnostics = false;

global Configuration respectSharing(Boolean respect) {
sharing = respect ? SharingMode.WITH : SharingMode.WITHOUT;
return this;
}

global Configuration printAst() {
printAst = true;
return this;
}

global Configuration withDiagnostics() {
withDiagnostics = true;
return this;
}

public void subscribe(EvaluatorEventNotifier notifier) {
// Always subscribe to the event that sets the sharing mode
// at the beginning of the evaluation regardless of configuration.
notifier.subscribe(OnEvaluationStartEvent.class, new EvaluationSharingModeSetter());

if (this.printAst) {
notifier.subscribe(OnAfterParseEvent.class, new AstPrinter());
}

if (this.withDiagnostics) {
EvaluationDiagnosticsListener diagnosticsListener = new EvaluationDiagnosticsListener();
notifier.subscribe(OnEvaluationStartEvent.class, diagnosticsListener);
notifier.subscribe(OnEvaluationEndEvent.class, diagnosticsListener);
}
}
}
10 changes: 10 additions & 0 deletions expression-src/main/api/EvaluationResult.cls
cesarParra marked this conversation as resolved.
Show resolved Hide resolved
cesarParra marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
global with sharing class EvaluationResult {
global Object result { get; private set; }

public Map<String, Object> additionalData { get; private set; }

public EvaluationResult(Object result, Map<String, Object> additionalData) {
this.result = result;
this.additionalData = additionalData;
}
}
72 changes: 9 additions & 63 deletions expression-src/main/api/Evaluator.cls
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
/**
* @description Evaluates a formula and returns the result.
*/
global with sharing class Evaluator {
public static SharingMode evaluationSharingMode;

global with sharing abstract class Evaluator {
/**
* @description Evaluates a formula and returns the result.
* @param formula The formula to evaluate.
* @return The result of the formula.
*/
global static Object run(String formula) {
return run(formula, new Config());
return run(formula, new Configuration());
}

/**
Expand All @@ -20,7 +18,7 @@ global with sharing class Evaluator {
* the evaluation.
* @return The result of the formula.
*/
global static Object run(String formula, Config config) {
global static Object run(String formula, Configuration config) {
return run(formula, (SObject) null, config);
}

Expand All @@ -33,7 +31,7 @@ global with sharing class Evaluator {
* @return The result of the formula.
*/
global static Object run(String formula, SObject context) {
return run(formula, context, new Config());
return run(formula, context, new Configuration());
}

/**
Expand All @@ -46,9 +44,8 @@ global with sharing class Evaluator {
* the evaluation.
* @return The result of the formula.
*/
global static Object run(String formula, SObject context, Config config) {
Expr pipedExpression = parseExpression(formula, config);
return interpretWithContext(context, pipedExpression, config);
global static Object run(String formula, SObject context, Configuration config) {
return EvaluatorResolver.forRecord(context).evaluate(formula, config).result;
}

/**
Expand All @@ -60,7 +57,7 @@ global with sharing class Evaluator {
* @return The result of the formula.
*/
global static Object run(String formula, Id recordId) {
return run(formula, recordId, new Config());
return run(formula, recordId, new Configuration());
}

/**
Expand All @@ -73,58 +70,7 @@ global with sharing class Evaluator {
* the evaluation.
* @return The result of the formula.
*/
global static Object run(String formula, Id recordId, Config config) {
Expr pipedExpression = parseExpression(formula, config);
SObject record = getRecord(recordId, pipedExpression);
return interpretWithContext(record, pipedExpression, config);
}

private static Expr parseExpression(String formula, Config config) {
evaluationSharingMode = config.sharing;
Scanner scanner = new Scanner(formula);
List<Token> tokens = scanner.scanTokens();

Parser parser = new Parser(tokens);
Expr expression = parser.parse();

if (config.printAst) {
AstPrinter printer = new AstPrinter();
printer.printAst(expression);
}

PipeResolver pipeInterpreter = new PipeResolver();
Expr pipedExpression = pipeInterpreter.resolve(expression);
return pipedExpression;
}

private static SObject getRecord(Id recordId, Expr pipedExpression) {
ContextResolver ctxInterpreter = new ContextResolver(recordId);
return ctxInterpreter.build(pipedExpression);
}

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

global enum SharingMode {
WITH,
WITHOUT
}

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;
}
global static Object run(String formula, Id recordId, Configuration config) {
return EvaluatorResolver.forId(recordId).evaluate(formula, config).result;
}
}
21 changes: 14 additions & 7 deletions expression-src/main/editor/controllers/PlaygroundController.cls
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,20 @@ public with sharing class PlaygroundController {
@AuraEnabled
public static Result validate(String expr, Id recordId) {
Result toReturn = new Result();
Evaluator.Config config = new Evaluator.Config();
config.withDiagnostics = true;
Configuration config = new Configuration().withDiagnostics().printAst();
try {
EvaluationResult evalResult;
if (String.isNotBlank(recordId)) {
toReturn.result = Evaluator.run(expr, recordId, config);
evalResult = EvaluatorResolver.forId(recordId).evaluate(expr, config);
} else {
toReturn.result = Evaluator.run(expr, config);
evalResult = EvaluatorResolver.withoutContext().evaluate(expr, config);
}

toReturn.result = evalResult.result;
EvaluationDiagnosticsListener.DiagnosticsResult diagnosticsResult =
(EvaluationDiagnosticsListener.DiagnosticsResult)evalResult.additionalData.get('diagnostics');
toReturn.diagnostics = new Diagnostics(diagnosticsResult);
toReturn.ast = evalResult.additionalData.get('ast');
} catch (Exceptions.PositionAwareException e) {
EvaluationError error = new EvaluationError();
error.message = e.getMessage();
Expand All @@ -60,8 +66,6 @@ public with sharing class PlaygroundController {
toReturn.error = error;
}

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

return toReturn;
}

Expand All @@ -74,6 +78,9 @@ public with sharing class PlaygroundController {

@AuraEnabled
public Diagnostics diagnostics;

@AuraEnabled
public Object ast;
}

public class Diagnostics {
Expand All @@ -89,7 +96,7 @@ public with sharing class PlaygroundController {
@AuraEnabled
public Integer queryRows;

public Diagnostics(DiagnosticsDecorator.DiagnosticsResult result) {
public Diagnostics(EvaluationDiagnosticsListener.DiagnosticsResult result) {
this.cpuTime = result?.cpuTime;
this.dmlStatements = result?.dmlStatements;
this.queries = result?.queries;
Expand Down
6 changes: 6 additions & 0 deletions expression-src/main/editor/lwc/playground/playground.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@
<li><strong>Query Rows:</strong> {diagnostics.queryRows}</li>
</ul>
</lightning-tab>
<lightning-tab label="AST">
<pre class={resultColor}>
<lightning-formatted-rich-text
value={ast}></lightning-formatted-rich-text>
</pre>
</lightning-tab>
</lightning-tabset>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions expression-src/main/editor/lwc/playground/playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default class Monaco extends LightningElement {
iframeUrl = `${monaco}/main.html`;
result = {};
diagnostics = {};
ast = {};

async iframeLoaded() {
const functionKeywords = await getFunctions();
Expand Down Expand Up @@ -41,6 +42,7 @@ export default class Monaco extends LightningElement {
}

this.diagnostics = result.diagnostics;
this.ast = this._syntaxHighlight(JSON.stringify(result.ast, null, 4));
}

handleInputChange(event) {
Expand Down
2 changes: 1 addition & 1 deletion expression-src/main/src/helpers/QRunner.cls
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public inherited sharing abstract class QRunner {
return mockRunner;
}

if (Evaluator.evaluationSharingMode == Evaluator.SharingMode.WITH) {
if (Configuration.evaluationSharingMode == Configuration.SharingMode.WITH) {
return new WithSharingRunner();
} else {
return new WithoutSharingRunner();
Expand Down
46 changes: 0 additions & 46 deletions expression-src/main/src/interpreter/DiagnosticsDecorator.cls

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
// TODO: Rename and move to a more appropriate location
cesarParra marked this conversation as resolved.
Show resolved Hide resolved
@IsTest
private class DiagnosticsDecoratorTest {
@IsTest
static void populatesDiagnosticsWhenRun() {
String expression = '1 + 2';
Evaluator.Config configWithDiagnostics = new Evaluator.Config();
configWithDiagnostics.withDiagnostics = true;
Configuration configWithDiagnostics = new Configuration().withDiagnostics();
EvaluationResult result = EvaluatorResolver.withoutContext().evaluate(expression, configWithDiagnostics);

Object result = Evaluator.run(expression, configWithDiagnostics);
Assert.isNotNull(result.additionalData.get('diagnostics'));

Assert.areEqual(3, result);
Assert.isNotNull(DiagnosticsDecorator.result.cpuTime);
Assert.isNotNull(DiagnosticsDecorator.result.dmlStatements);
Assert.isNotNull(DiagnosticsDecorator.result.queries);
Assert.isNotNull(DiagnosticsDecorator.result.queryRows);
EvaluationDiagnosticsListener.DiagnosticsResult diagnostics =
(EvaluationDiagnosticsListener.DiagnosticsResult) result.additionalData.get('diagnostics');
Assert.isNotNull(diagnostics.cpuTime);
Assert.isNotNull(diagnostics.dmlStatements);
Assert.isNotNull(diagnostics.queries);
Assert.isNotNull(diagnostics.queryRows);
}
}
3 changes: 3 additions & 0 deletions expression-src/main/src/resolver/EvaluatorEventListener.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public interface EvaluatorEventListener {
Map<String, Object> handle(EvaluatorEvent event);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>58.0</apiVersion>
<status>Active</status>
</ApexClass>
33 changes: 33 additions & 0 deletions expression-src/main/src/resolver/EvaluatorEventNotifier.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
public with sharing class EvaluatorEventNotifier {
final Map<Type, List<EvaluatorEventListener>> listeners =
new Map<Type, List<EvaluatorEventListener>>();

// Holds any data received from the event handler.
final Map<String, Object> listenerData;

public EvaluatorEventNotifier(Map<String, Object> listenerData) {
this.listenerData = listenerData;
}

public void subscribe(Type eventType, EvaluatorEventListener listener) {
List<EvaluatorEventListener> users = listeners.get(eventType);
if (users == null) {
users = new List<EvaluatorEventListener>();
listeners.put(eventType, users);
}
users.add(listener);
}

public void notify(EvaluatorEvent event) {
Type eventType = event.getType();
List<EvaluatorEventListener> users = listeners.get(eventType);
if (users != null) {
for (EvaluatorEventListener listener : users) {
Map<String, Object> handlerResult = listener.handle(event);
if (handlerResult != null) {
listenerData.putAll(handlerResult);
}
}
}
}
}
Loading
Loading