Closed paustint closed 3 years ago
@pgonzaleznetwork - I have an initial version of this developed on #156, but was hoping you could provide some additional example apex queries that I can add test-cases for - or at least provide your feedback on what I have implemented so far to see if changes should be made.
Right now, the general logic is like this for apex variables after a :
new
keyword, with optional generic (this is only allowed as the very first item)
Allow these to repeat as many times as needed, separated by .
:Known gaps:
new
keyword are not allowed as arguments to function calls (probably easy to add this)Here are some test cases I have written. You can see #113
has invalid apex and that WHERE clause is stripped off, but all the other queries are parsed correctly.
{
testCase: 112,
options: { allowApexBindVariables: true, ignoreParseErrors: false },
soql: `SELECT Id, (SELECT Id FROM Contacts WHERE Id IN :contactMap.keySet()) FROM Account WHERE Id IN :accountMap.keySet()`,
output: {
fields: [
{
type: 'Field',
field: 'Id',
},
{
type: 'FieldSubquery',
subquery: {
fields: [{ type: 'Field', field: 'Id' }],
relationshipName: 'Contacts',
where: { left: { field: 'Id', literalType: 'APEX_BIND_VARIABLE', operator: 'IN', value: 'contactMap.keySet()' } },
},
},
],
sObject: 'Account',
where: { left: { field: 'Id', literalType: 'APEX_BIND_VARIABLE', operator: 'IN', value: 'accountMap.keySet()' } },
},
},
{
testCase: 113,
options: { allowApexBindVariables: true, ignoreParseErrors: true },
soql: `SELECT Id, (SELECT Id FROM Contacts WHERE Id IN :contact_900Map.keySet()) FROM Account WHERE Id IN :acco INVALID untMap.keySet()`,
soqlComposed: `SELECT Id, (SELECT Id FROM Contacts WHERE Id IN :contact_900Map.keySet()) FROM Account`,
output: {
fields: [
{
type: 'Field',
field: 'Id',
},
{
type: 'FieldSubquery',
subquery: {
fields: [{ type: 'Field', field: 'Id' }],
relationshipName: 'Contacts',
where: { left: { field: 'Id', literalType: 'APEX_BIND_VARIABLE', operator: 'IN', value: 'contact_900Map.keySet()' } },
},
},
],
sObject: 'Account',
},
},
{
testCase: 114,
options: { allowApexBindVariables: true, ignoreParseErrors: false },
soql: `SELECT Id FROM Account WHERE Id IN :new Map<Id, SObject>(someVar).keySet()`,
output: {
fields: [
{
type: 'Field',
field: 'Id',
},
],
sObject: 'Account',
where: { left: { field: 'Id', literalType: 'APEX_BIND_VARIABLE', operator: 'IN', value: 'new Map<Id, SObject>(someVar).keySet()' } },
},
},
{
testCase: 115,
options: { allowApexBindVariables: true, ignoreParseErrors: false },
soql: `SELECT Id FROM Account WHERE Id IN :new Map<Id, SObject>(someVar).getSomeClass().records`,
output: {
fields: [
{
type: 'Field',
field: 'Id',
},
],
sObject: 'Account',
where: {
left: {
field: 'Id',
literalType: 'APEX_BIND_VARIABLE',
operator: 'IN',
value: 'new Map<Id, SObject>(someVar).getSomeClass().records',
},
},
},
},
Added parsing support for accessing variables by index
Ex: SELECT Id FROM Opportunity WHERE SBQQ__StandardTerm__c = :quotes[3].SBQQ__QuoteLine__r[0].Term__c
@pgonzaleznetwork - I am going to merge and publish #156 - if you encounter additional apex expressions that are not parsed correctly, please open a new ticket.
Thank you!
@paustint sorry for the late reply on this. This looks great and I think it covers the use cases we care about. When it comes to HappySoup.io, the most important part is to be able to get the fields in the SELECT or WHERE clause, but we don't care too much about what the actual filter value is i.e
WHERE SomeField = :myExpression()
Here, we care about SomeField and not really about the value of the expression. For our use case, what matters is that the parseQuery function doesn't fail all together, and what you've done so far satisfies that use case.
Thank you so much!
Carried over from #153
Description
Apex bind variables only support the most basic syntax, which should be improved to support additional common use-cases.
:someVarInstance.foo(bar, baz, ...)
Allow function calls with varying number of arguments:new Map<Id, SObject>(someVar).keySet()
Allow new constructors with generics and arguments with chained method calls