Skip to content

Commit 9be492f

Browse files
authored
Merge pull request #62 from paustint/enhancement-61
WITH SECURITY_ENFORCED is not supported #61
2 parents a14ee29 + c8662b1 commit 9be492f

18 files changed

+2245
-1984
lines changed

.prettierrc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"printWidth": 120,
3-
"parser": "typescript",
43
"semi": true,
54
"tabWidth": 2,
65
"useTabs": false,

CHANGELOG.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,35 @@
11
# Changelog
22

3+
## 1.1.0
4+
5+
- Updated `Contributing.md` with more detailed instructions on grammar updates
6+
- Added support for `WITH SECURITY_ENFORCED` (#61)
7+
38
## 1.0.2
4-
- If a field in a query happened to have a function reserved word, such as `Format`, then parsing the query failed. (#59)
9+
10+
- If a field in a query happened to have a function reserved word, such as `Format`, then parsing the query failed. (#59)
511

612
## 1.0.1
13+
714
- Ensured that nothing is logged directly to the console unless logging is enabled
815

916
## 1.0.0
17+
1018
### Changed
19+
1120
**!BREAKING CHANGES!**
21+
1222
- Added literal type information to fields to provide additional information about the field type. (#51)
1323
- WHERE clause fields have one of the following types `'STRING' | 'INTEGER' | 'DECIMAL' | 'BOOLEAN' | 'NULL' | 'DATE_LITERAL' | 'DATE_N_LITERAL';` stored in the condition.
14-
- For date literal fields that have variables, `dateLiteralVariable` will be populated with the value
24+
- For date literal fields that have variables, `dateLiteralVariable` will be populated with the value
1525
- Modified Field data structure to have explicit type information. (#46, #52)
1626
- The data structure for fields has been modified to include specific information about the structure of a given field to ease making sense of a parsed query,
1727
- To aid in creating compose fields, a new helper method is available - `getComposedField()`. This takes in a simple data structure (or even a string) and will return the structure needed to compose a query.
1828

1929
### New
30+
2031
- An additional `queryUtils` object is available with the following functions:
2132
- `function getComposedField(input: string | ComposeFieldInput): FieldType`
2233
- `function getFlattenedFields(query: Query, isAggregateResult?: boolean): string[]`
2334
- `function isSubquery(query: Query | Subquery): query is Subquery`
24-
- Look at the README and refer to the unit tests for example usage.
35+
- Look at the README and refer to the unit tests for example usage.

CONTRIBUTING.md

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,46 @@
11
# How to contribute
2+
23
Contributions of any kind are welcome and appreciated.
34

45
# What features are allowed
6+
57
Any feature that is part of the core project and does not deviate from what the product is will be considered t obe accepted.
68

79
# Making Changes
8-
* For the repository
9-
* Create a new branch from master (usually) and with a meaningful name for your changes
10-
* Make you changes
11-
* Commit and push your change
12-
* Please run `prettier` on all modified files prior to opening your pull request
13-
* open a Pull Request for the master branch
10+
11+
- For the repository
12+
- Create a new branch from master (usually) and with a meaningful name for your changes
13+
- Make you changes
14+
- Commit and push your change
15+
- Please run `prettier` on all modified files prior to opening your pull request
16+
- open a Pull Request for the master branch
17+
18+
# Re-generating parse after grammar change
19+
20+
:beetle: There is a bug in the generated ANTLR output that must be corrected by hand in the generated output to make this work.
21+
22+
- Note: I am not an expert with language parsing/antler and I did not create the grammar, so I was not able to figure this bug out. I believe the problem relies with `antlr4ts-cli` and the way imports are always added to the end of the file
23+
24+
## Generate lexer and parser
25+
26+
- Run `npm run antlr`
27+
- This will remove and re-generate all of the lexer and parser files required to parse SOQL
28+
- :beetle: Manual steps:
29+
- Open `SOQLParser.ts` after the file generation
30+
- Find the class `Keywords_alias_allowedContext` somewhere near line ~44K
31+
- Cut this class and all remaining classes through the end of the file and paste right underneath the `import` statements
32+
- Run all unit tests afterwards `npm run test` - if anything is broken, please troubleshoot or create a PR and ask for assistance
33+
34+
## Update code to support new grammar
35+
36+
- Start with adding a new unit test
37+
- This will ensure the parser and composer support the syntax
38+
- Update the parser to listen for the new grammar (this depends on grammar change that was implemented)
39+
- Assuming there is a new clause somewhere, look in `lib/generated/SOQLListener.ts` to find the new enter/exit methods that were added
40+
- Implement these methods in `lib/SOQLListener.ts` to handle parsing the logic
41+
- Update `lib/SOQLComposer.ts` to handle creating the SOQL query from the parsed query
42+
43+
## Unit tests
44+
45+
- ensure your new unit tests cover all the cases
46+
- Ensure unit tests are all passing, or open a PR and ask for assistance

CONTRIBUTORS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
* Austin Turner @paustint
2-
* Alfredo Benassi @ABenassi87
1+
- Austin Turner @paustint
2+
- Alfredo Benassi @ABenassi87

README.md

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,37 @@
55
[![dependencies](https://david-dm.org/paustint/soql-parser-js.svg)](https://david-dm.org/paustint/soql-parser-js)
66

77
## Description
8+
89
SOQL Parser JS provides the following capabilities:
10+
911
1. Parse a SOQL query into a usable data structure.
1012
2. Turn a parsed query data structure back into well a SOQL query with various format options.
1113
3. Check if a SOQL query is syntactically valid (**note**: some cases may be structurally sound but not allowed by SFDC).
1214

1315
This library is written in Typescript and all type definitions are included with the library for your benefit if you choose to use Typescript or use VSCode's automatic type checking.
1416

15-
*Warning*: antlr4 is dependency for this library and is a rather large library (~600 KB) and is required for the parser to function, use in the browser with care.
17+
_Warning_: antlr4 is dependency for this library and is a rather large library (~600 KB) and is required for the parser to function, use in the browser with care.
1618

1719
## Examples
20+
1821
For an example of the parser, check out the [example application](https://paustint.github.io/soql-parser-js/).
1922

2023
Have a look through the unit tests for many more examples.
2124

2225
# Usage
26+
2327
## Parsing
28+
2429
Parsing a SOQL query can be completed by calling `parseQuery(soqlQueryString, options)` and a `Query` data structure will be returned;
2530

2631
#### Typescript / ES6
32+
2733
```typescript
2834
import { parseQuery } from 'soql-parser-js'; // TS / ES6 imports
2935
// var soqlParserJs = require('soql-parser-js'); // node's require format - usage: soqlParserJs.parseQuery()
3036

31-
const soql = 'SELECT UserId, COUNT(Id) from LoginHistory WHERE LoginTime > 2010-09-20T22:16:30.000Z AND LoginTime < 2010-09-21T22:16:30.000Z GROUP BY UserId';
37+
const soql =
38+
'SELECT UserId, COUNT(Id) from LoginHistory WHERE LoginTime > 2010-09-20T22:16:30.000Z AND LoginTime < 2010-09-21T22:16:30.000Z GROUP BY UserId';
3239

3340
const soqlQuery = parseQuery(soql);
3441
// const soqlQuery = soqlParserJs.parseQuery(soql); // using require()
@@ -37,6 +44,7 @@ console.log(JSON.stringify(soqlQuery, null, 2));
3744
```
3845

3946
#### Options
47+
4048
```typescript
4149
export interface SoqlQueryConfig {
4250
continueIfErrors?: boolean; // default=false
@@ -45,16 +53,17 @@ export interface SoqlQueryConfig {
4553
```
4654

4755
## Composing
56+
4857
Composing a query will turn a Query object back to a SOQL query. The exact same data structure returned from `parseQuery()` can be used,
4958
but there are many use-cases where you may need to build your own data structure to compose a query.
5059
These examples show building your own Query object with the minimum required fields.
5160

5261
**Note:** For some operators, they may be converted to upper case (e.x. NOT, AND)
5362

54-
**Note:** There are a number of fields populated on the Query object when `parseQuery()` is called that are not required to compose a query. Look at the examples below and the comments in the data model for more information.
55-
63+
**Note:** There are a number of fields populated on the Query object when `parseQuery()` is called that are not required to compose a query. Look at the examples below and the comments in the data model for more information.
5664

5765
**The base query object is shaped like this:**
66+
5867
```typescript
5968
export interface QueryBase {
6069
fields: FieldType[];
@@ -74,7 +83,9 @@ export interface QueryBase {
7483
The easiest way to build the fields is to call the utility function `getComposedField()`.
7584

7685
### Example
86+
7787
This is the query that will be composed
88+
7889
```sql
7990
SELECT Id, Name, FORMAT(Amount) MyFormattedAmount,
8091
(SELECT Quantity, ListPrice, PricebookEntry.UnitPrice, PricebookEntry.Name FROM OpportunityLineItems)
@@ -139,11 +150,11 @@ const soqlQuery = {
139150
const composedQuery = composeQuery(soqlQuery, { format: true });
140151

141152
console.log(composedQuery);
142-
143153
```
144154

145-
In the above examples, we made use of `getComposedField(input: string | ComposeFieldInput)` to help easily compose the fields. The input expects a string or one of the following shapes of data below. An error will be thrown if the data passed in is not one of the following shapes:
155+
In the above examples, we made use of `getComposedField(input: string | ComposeFieldInput)` to help easily compose the fields. The input expects a string or one of the following shapes of data below. An error will be thrown if the data passed in is not one of the following shapes:
146156
and will return a `FieldType` object.
157+
147158
```typescript
148159
export interface ComposeField {
149160
field: string;
@@ -173,10 +184,12 @@ export interface ComposeFieldTypeof {
173184
```
174185

175186
## Checking if a Query is Valid
187+
176188
This will parse the AST tree to confirm the syntax is valid, but will not parse the tree into a data structure.
177189
This method is faster than parsing the full query.
178190

179191
Options:
192+
180193
```typescript
181194
export interface ConfigBase {
182195
logging: boolean; // default=false
@@ -186,26 +199,29 @@ export interface ConfigBase {
186199
```typescript
187200
import { isQueryValid } from 'soql-parser-js';
188201

189-
const soql = 'SELECT UserId, COUNT(Id) from LoginHistory WHERE LoginTime > 2010-09-20T22:16:30.000Z AND LoginTime < 2010-09-21T22:16:30.000Z GROUP BY UserId';
202+
const soql =
203+
'SELECT UserId, COUNT(Id) from LoginHistory WHERE LoginTime > 2010-09-20T22:16:30.000Z AND LoginTime < 2010-09-21T22:16:30.000Z GROUP BY UserId';
190204

191205
const isValid = isQueryValid(soql);
192206

193207
console.log('isValid', isValid);
194-
195208
```
196209

197210
#### Node
211+
198212
```javascript
199213
var soqlParserJs = require('soql-parser-js');
200214

201-
const soql = 'SELECT UserId, COUNT(Id) from LoginHistory WHERE LoginTime > 2010-09-20T22:16:30.000Z AND LoginTime < 2010-09-21T22:16:30.000Z GROUP BY UserId';
215+
const soql =
216+
'SELECT UserId, COUNT(Id) from LoginHistory WHERE LoginTime > 2010-09-20T22:16:30.000Z AND LoginTime < 2010-09-21T22:16:30.000Z GROUP BY UserId';
202217

203218
const isValid = isQueryValid(soql);
204219

205220
console.log('isValid', isValid);
206221
```
207222

208223
## Format Query
224+
209225
This function is provided as a convenience and just calls parse and compose under the hood.
210226

211227
```typescript
@@ -214,9 +230,12 @@ import { formatQuery } from 'soql-parser-js';
214230
const query = `SELECT Id, Name, AccountNumber, AccountSource, AnnualRevenue, BillingAddress, BillingCity, BillingCountry, BillingGeocodeAccuracy, ShippingStreet, Sic, SicDesc, Site, SystemModstamp, TickerSymbol, Type, Website, (SELECT Id, Name, AccountId, Amount, CampaignId, CloseDate, CreatedById, Type FROM Opportunities), (SELECT Id, Name, AccountNumber, AccountSource, AnnualRevenue, BillingAddress, Website FROM ChildAccounts) FROM Account WHERE Name LIKE 'a%' OR Name LIKE 'b%' OR Name LIKE 'c%'`;
215231

216232
const formattedQuery1 = formatQuery(query);
217-
const formattedQuery2 = formatQuery(query, { fieldMaxLineLen: 20, fieldSubqueryParensOnOwnLine: false, whereClauseOperatorsIndented: true });
233+
const formattedQuery2 = formatQuery(query, {
234+
fieldMaxLineLen: 20,
235+
fieldSubqueryParensOnOwnLine: false,
236+
whereClauseOperatorsIndented: true,
237+
});
218238
const formattedQuery3 = formatQuery(query, { fieldSubqueryParensOnOwnLine: true, whereClauseOperatorsIndented: true });
219-
220239
```
221240

222241
```sql
@@ -310,18 +329,22 @@ export interface FormatOptions {
310329
```
311330

312331
## Utility Functions
332+
313333
The following utility functions are available:
314-
1. `getComposedField(input: string | ComposeFieldInput)`
315-
1. Convenience method to construct fields in the correct data format. See example usage in the Compose example.
316-
2. `isSubquery(query: Query | Subquery)`
317-
1. Returns true if the data passed in is a subquery
318-
3. `getFlattenedFields(query: Query)`
319-
1. This provides a list of fields that are stringified and flattened in order to access data from a returned API call from Salesforce. Refer to `tests/publicUtils.spec.ts` for usage examples.
320334

335+
1. `getComposedField(input: string | ComposeFieldInput)`
336+
1. Convenience method to construct fields in the correct data format. See example usage in the Compose example.
337+
1. `isSubquery(query: Query | Subquery)`
338+
1. Returns true if the data passed in is a subquery
339+
1. `getFlattenedFields(query: Query)`
340+
1. This provides a list of fields that are stringified and flattened in order to access data from a returned API call from Salesforce. Refer to `tests/publicUtils.spec.ts` for usage examples.
321341

322342
## Data Models
343+
323344
### Query
345+
324346
These are all available for import in your typescript projects
347+
325348
```typescript
326349
export type LogicalOperator = 'AND' | 'OR';
327350
export type Operator = '=' | '!=' | '<=' | '>=' | '>' | '<' | 'LIKE' | 'IN' | 'NOT IN' | 'INCLUDES' | 'EXCLUDES';
@@ -473,11 +496,10 @@ export interface WithDataCategoryCondition {
473496
selector: GroupSelector;
474497
parameters: string[];
475498
}
476-
477499
```
478500

479-
480501
### Utils
502+
481503
```typescript
482504
export interface ComposeField {
483505
field: string;
@@ -514,9 +536,11 @@ export type ComposeFieldInput =
514536
```
515537

516538
## CLI Usage
539+
517540
The CLI can be used to parse a query or compose a previously parsed query back to SOQL.
518541

519542
**Examples:**
543+
520544
```shell
521545
$ npm install -g soql-parser-js
522546
$ soql --help
@@ -531,6 +555,7 @@ $ soql -compose some-input-file.json -output some-output-file.json
531555
```
532556

533557
**Arguments:**
558+
534559
```
535560
--query, -q A SOQL query surrounded in quotes or a file path to a text file containing a SOQL query.
536561
--compose, -c An escaped and quoted parsed SOQL JSON string or a file path to a text file containing a parsed query JSON object.
@@ -541,8 +566,10 @@ $ soql -compose some-input-file.json -output some-output-file.json
541566
```
542567

543568
## Contributing
569+
544570
All contributions are welcome on the project. Please read the [contribution guidelines](https://github.com/paustint/soql-parser-js/blob/master/CONTRIBUTING.md).
545571

546572
## Special Thanks
547-
* This library is based on the ANTLR4 grammar file [produced by Mulesoft](https://github.com/mulesoft/salesforce-soql-parser/blob/antlr4/SOQL.g4).
548-
* The following repository also was a help to get things started: https://github.com/petermetz/antlr-4-ts-test
573+
574+
- This library is based on the ANTLR4 grammar file [produced by Mulesoft](https://github.com/mulesoft/salesforce-soql-parser/blob/antlr4/SOQL.g4).
575+
- The following repository also was a help to get things started: https://github.com/petermetz/antlr-4-ts-test

0 commit comments

Comments
 (0)