soql-parser-js
Advanced tools
Comparing version 1.2.1 to 2.0.0-rc.1
182
CHANGELOG.md
# Changelog | ||
## 2.0.0 | ||
### Summary | ||
Version 2.0 brings some significant bundle size and performance improvements. This library now uses [Chevrotain](https://github.com/SAP/chevrotain) instead of [antlr4](https://github.com/antlr/antlr4). With this change, everything related to parsing had to be re-written from scratch. Chevrotain uses pure javascript to handle lexing, parsing, and visiting the generated ast/cst as opposed to using a grammar file and generating a javascript parser based on the grammar. | ||
With this change, the data model was reviewed and analyzed, and there are some significant breaking changes to the data structures. Review the 🔥breaking changes🔥 below for a detailed description of each breaking change. | ||
#### Bundle Size | ||
`soql-parser-js` bundles all of the library code and two dependencies `chevrotain` and `regexp-to-ast` (required by chevrotain) into the javascript bundle. Previously, `antlr4` was not bundled and was required to be installed separately. | ||
The following bundle size uses the default webpack configuration (e.x. `npx webpack`) with no webpack configuration explicitly defined. | ||
```javascript | ||
var soqlParser = require('soql-parser-js'); | ||
const query = soqlParser.parseQuery(`SELECT Id FROM Account WHERE Id = 'FOO'`); | ||
console.log('query', query); | ||
const soql = soqlParser.composeQuery(query); | ||
console.log('soql', soql); | ||
``` | ||
- Version 1.x: **545kb** | ||
- Version 2.0: **197kb** | ||
#### Benchmarks | ||
Here is an example benchmark of parsing all the unit tests 1,000 times | ||
``` | ||
OLD PARSER: ~6.2 seconds for ~60K parses | ||
NEW PARSER AFTER OPTIMIZATION: ~2.25 seconds for 60K parses | ||
``` | ||
### Breaking Changes 🔥 | ||
#### General Changes | ||
- The CLI was removed. | ||
- The `parseQuery()` function no longer accepts `options` as a second parameter. | ||
- `rawValue` will always have a space between parameters `GROUPING(Id, BillingCountry)` | ||
- Some `literalType` values may have differing case from prior versions, regardless of the data input. | ||
- `TRUE`, `FALSE`, and all functions except those listed below will always be returned in uppercase, regardless of case of input. | ||
- **Exceptions**: | ||
- `toLabel`, `convertTimezone`, `convertCurrency` will always be in camelCase. | ||
- Added new available types for `DateLiteral` and `DateNLiteral`. | ||
#### Compose Query | ||
- `getComposedField()` is deprecated, you should now use `getField()`. `getComposedField()` will remain available for backward compatibility. | ||
- `getField()`/`getComposedField()` has the following changes: | ||
1. `fn` property is has been deprecated (but still exists), you should now use `functionName` instead. | ||
2. The `from` property has been removed for subqueries. The `relationshipName` is required to be populated to compose a subquery. | ||
- On the FormatOptions interface `fieldMaxLineLen` was renamed to `fieldMaxLineLength`. | ||
```diff | ||
export interface FormatOptions { | ||
numIndent?: number; | ||
- fieldMaxLineLen?: number; | ||
+ fieldMaxLineLength?: number; | ||
fieldSubqueryParensOnOwnLine?: boolean; | ||
whereClauseOperatorsIndented?: boolean; | ||
logging?: boolean; | ||
} | ||
``` | ||
#### Parse Query | ||
- `rawValue` will now be included on `Field` if `objectPrefix` is defined. | ||
- `alias` may be included on `Field`, if defined. | ||
- On `FieldFunctionExpression`, `fn` was renamed to `functionName`. this was done because all other usages of `fn` were `FunctionExp`, but it was a string in this case. | ||
- The `parameters` type on `FieldFunctionExpression` was modified to allow an array of varying types. | ||
- Removed `from` property from `FieldSubquery`. | ||
- `having` was removed from `QueryBase` and now lives as a property on `GroupByClause`. | ||
- On the `Condition` object, `literalType` may be an array. This will be an array if `value` is an array and there are variable types within the `value`. For example: `WHERE Foo IN ('a', null, 'b')` would produce `literalType: ['STRING', 'NULL', 'STRING']`. | ||
- The `GroupByClause` has the following modifications: | ||
- `field` is now optional, and will be populated only if the grouping is on a single field. | ||
- `type` has been renamed to `fn` and will be populated when `CUBE` and `ROLLUP` are used. | ||
- The `having` clause has been moved as a top-level property to the `GroupByClause` and will be populated only if a `having` clause is present. | ||
- The `HavingCondition` now has a `literalType` that will be populated with the type of the `value` property. | ||
- `FunctionExp` has the following modifications | ||
- `text` was renamed to `rawValue` to be more consistent with other places in the data model. | ||
- `name` was renamed to `functionName`. | ||
- `parameter` was renamed to `parameters` and the type was changed to `(string | FunctionExp)[]` to support nested functions. This will ALWAYS be an array now even if there is only one parameter. | ||
```diff | ||
export interface Field { | ||
type: 'Field'; | ||
field: string; | ||
objectPrefix?: string; | ||
+ rawValue?: string; | ||
+ alias?: string; | ||
} | ||
export interface FieldFunctionExpression { | ||
type: 'FieldFunctionExpression'; | ||
- fn: string; | ||
+ functionName: string; | ||
- parameters?: string[] | FieldFunctionExpression[]; | ||
+ parameters?: (string | FieldFunctionExpression)[]; | ||
alias?: string; | ||
isAggregateFn?: boolean; | ||
rawValue?: string; | ||
} | ||
export interface FieldRelationship { | ||
type: 'FieldRelationship'; | ||
field: string; | ||
relationships: string[]; | ||
objectPrefix?: string; | ||
rawValue?: string; | ||
+ alias?: string; | ||
} | ||
export interface FieldSubquery { | ||
type: 'FieldSubquery'; | ||
subquery: Subquery; | ||
- from?: string; | ||
} | ||
export interface QueryBase { | ||
fields: FieldType[]; | ||
sObjectAlias?: string; | ||
where?: WhereClause; | ||
limit?: number; | ||
offset?: number; | ||
groupBy?: GroupByClause; | ||
- having?: HavingClause; | ||
orderBy?: OrderByClause | OrderByClause[]; | ||
withDataCategory?: WithDataCategoryClause; | ||
withSecurityEnforced?: boolean; | ||
for?: ForClause; | ||
update?: UpdateClause; | ||
} | ||
export interface Condition { | ||
openParen?: number; | ||
closeParen?: number; | ||
logicalPrefix?: LogicalPrefix; | ||
field?: string; | ||
fn?: FunctionExp; | ||
operator: Operator; | ||
value?: string | string[]; | ||
valueQuery?: Query; | ||
- literalType?: LiteralType; | ||
+ literalType?: LiteralType | LiteralType[]; | ||
dateLiteralVariable?: number;parsed | ||
} | ||
export interface GroupByClause { | ||
- field: string | string[]; | ||
+ field?: string | string[]; | ||
- type?: GroupByType; | ||
+ fn?: FunctionExp; | ||
+ having?: HavingClause; | ||
} | ||
export interface HavingCondition { | ||
openParen?: number; | ||
closeParen?: number; | ||
field?: string; | ||
fn?: FunctionExp; | ||
operator: string; | ||
value: string | number; | ||
+ literalType?: String; | ||
} | ||
export interface FunctionExp { | ||
- text?: string; | ||
+ rawValue?: string; | ||
- name?: string; | ||
+ functionName?: string; | ||
alias?: string; | ||
- parameter?: string | string[]; | ||
+ parameters?: (string | FunctionExp)[]; | ||
isAggregateFn?: boolean; | ||
- fn?: FunctionExp; | ||
} | ||
``` | ||
## 1.2.1 | ||
@@ -4,0 +186,0 @@ |
{ | ||
"name": "soql-parser-js", | ||
"version": "1.2.1", | ||
"description": "Salesforce.com SOQL parser.", | ||
"version": "2.0.0-rc.1", | ||
"description": "Salesforce.com SOQL parser and composer", | ||
"main": "dist/index.js", | ||
"module": "dist/index.es.js", | ||
"types": "dist/index.d.ts", | ||
"bin": { | ||
"soql": "dist/cli.js" | ||
}, | ||
"module": "dist_esm/index.js", | ||
"types": "dist/src/index.d.ts", | ||
"scripts": { | ||
"clean": "rm -rf ./dist/*", | ||
"test": "TS_NODE_PROJECT=\"test/tsconfig.test.json\" mocha -r ts-node/register test/**/*.spec.ts -P test/tsconfig.test.json", | ||
"debug": "TS_NODE_PROJECT=\"tsconfig.json\" ts-node ./debug/test.ts -P tsconfig.json", | ||
"clean:esm": "rm -rf ./dist_esm/*", | ||
"prebuild": "npm run clean", | ||
"build": "rollup -c", | ||
"build": "npm-run-all compile bundle:regular bundle:min build:esm", | ||
"build:esm": "npm-run-all clean:esm compile:esm", | ||
"compile": "tsc", | ||
"compile:esm": "tsc --project tsconfig-esm.json", | ||
"bundle:regular": "webpack --config webpack-regular.config.js", | ||
"bundle:min": "webpack --config webpack-min.config.js", | ||
"tsc": "./node_modules/.bin/tsc", | ||
"preantlr": "rm -rf lib/generated/*", | ||
"antlr": "npm run preantlr && ./node_modules/.bin/antlr4ts -visitor SOQL.g4 -o lib/generated/", | ||
"release": "release-it", | ||
"update-docs": "cd docs && npm run update", | ||
"update-docs:no-git": "cd docs && npm run deploy" | ||
"test": "TS_NODE_PROJECT=\"test/tsconfig.json\" mocha --timeout 10000 -r ts-node/register test/**/*.spec.ts -P test/tsconfig.json" | ||
}, | ||
"author": "Austin Turner <paustint@gmail.com>", | ||
"license": "MIT", | ||
"files": [ | ||
"dist_esm/**/*.js", | ||
"lib/src/**/*.js", | ||
"lib/src/**/*.d.ts", | ||
"lib/index.js", | ||
"lib/index.min.js", | ||
"lib/main.licenses.txt", | ||
"README.md", | ||
"LICENSE.txt", | ||
"CHANGELOG.md" | ||
], | ||
"dependencies": { | ||
"@types/lodash": "^4.14.136", | ||
"antlr4ts": "0.4.1-alpha.0", | ||
"lodash": "^4.17.14", | ||
"minimist": "^1.2.0" | ||
"chevrotain": "^6.5.0", | ||
"lodash.get": "^4.4.2" | ||
}, | ||
"devDependencies": { | ||
"@types/chai": "^4.1.6", | ||
"@types/minimist": "^1.2.0", | ||
"@types/mocha": "^5.2.5", | ||
"@types/node": "^8.0.14", | ||
"antlr4ts-cli": "0.4.0-alpha.4", | ||
"@types/chai": "^4.2.3", | ||
"@types/lodash.get": "^4.4.6", | ||
"@types/mocha": "^5.2.7", | ||
"@types/node": "^12.7.5", | ||
"chai": "^4.1.2", | ||
"lodash.get": "^4.4.2", | ||
"magic-string": "^0.25.1", | ||
"chalk": "^2.4.2", | ||
"jsonfile": "^5.0.0", | ||
"license-webpack-plugin": "^2.1.2", | ||
"mocha": "^5.2.0", | ||
"release-it": "^12.3.3", | ||
"rollup": "^0.66.6", | ||
"rollup-plugin-cli": "^0.1.5", | ||
"rollup-plugin-typescript2": "^0.21.1", | ||
"npm-run-all": "^4.1.5", | ||
"release-it": "^12.4.1", | ||
"string-replace-loader": "^2.2.0", | ||
"ts-node": "^7.0.1", | ||
"typescript": "^2.4.1" | ||
"typescript": "^3.6.3", | ||
"webpack": "^4.40.2", | ||
"webpack-cli": "^3.3.9" | ||
}, | ||
@@ -52,3 +60,6 @@ "peerDependencies": {}, | ||
"soql", | ||
"Salesforce" | ||
"salesforce", | ||
"parse", | ||
"compose", | ||
"parser" | ||
], | ||
@@ -55,0 +66,0 @@ "repository": { |
396
README.md
@@ -9,28 +9,63 @@ # SOQL Parser JS | ||
:sunny: This library allows parsing Salesforce SOQL queries using JavaScript or Typescript. Works in the browser and node. :sunny: | ||
**This library allows parsing and composing SOQL queries from Salesforce using JavaScript or Typescript.** | ||
SOQL Parser JS provides the following capabilities: | ||
**Available Features:** | ||
1. Parse a SOQL query into a usable data structure. | ||
2. Turn a parsed data structure back into a well formed SOQL query with various format options. | ||
3. Check if a SOQL query is syntactically valid (**note**: some cases may be structurally valid but not allowed by Salesforce, e.x. invalid field name). | ||
1. Parse SOQL queries into a common `Query` data structure. | ||
2. Compose a `Query` data structure back into a SOQL query. | ||
3. Validate a query to check if the syntax is valid. (Note: even if a query is valid, it might still be invalid based on your org configuration) | ||
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. | ||
This library uses [Chevrotain](https://github.com/SAP/chevrotain) to parse queries. Prior to version 2.0.0, [antlr4](https://github.com/antlr/antlr4) was used. The move to Chevrotain provided a 35% performance improvement and resulted in a 36% decrease in bundle size. :tada: | ||
:warning: antlr4 is dependency for this library and is a rather large library (~600 KB) and is required for the parser to function. Consider using dynamic imports to achieve lazy loading. | ||
Want to try it out? [Check out the demo](https://paustint.github.io/soql-parser-js/). | ||
## Examples | ||
## Quick Start | ||
Want to try it out? [Check out the demo](https://paustint.github.io/soql-parser-js/). | ||
```javascript | ||
import { parseQuery, composeQuery, isQueryValid } from 'soql-parser-js'; | ||
Look through the [unit tests](./test/TestCases.ts) for additional examples. | ||
const query = parseQuery(`SELECT Id FROM Account WHERE Id = 'FOO'`); | ||
console.log('query', query); | ||
# Usage | ||
const soql = composeQuery(query); | ||
console.log('soql', soql); // SELECT Id FROM Account WHERE Id = 'FOO' | ||
## Parsing | ||
isQueryValid('SELECT Id, Foo FROM Baz'); // true | ||
isQueryValid('SELECT Id Foo FROM Baz'); // false | ||
``` | ||
Parsing a SOQL query can be completed by calling `parseQuery(soqlQueryString, options)`. A `Query` data structure will be returned; | ||
## Available Features | ||
#### Typescript / ES6 | ||
| Function | Description | Arguments | | ||
| ------------ | ------------------------------------------------------ | ------------------------------------------ | | ||
| parseQuery | Parse a SOQL query string into a Query data structure. | soql: Query | | ||
| isQueryValid | Returns true if the query was able to be parsed. | soql: Query | | ||
| composeQuery | Turn a Query object back into a SOQL statement | soql: Query<br> config?: SoqlComposeConfig | | ||
| formatQuery | Format a SOQL query string. | soql: Query<br> config?: FormatOptions | | ||
**SoqlComposeConfig** | ||
| Property | Type | Description | required | default | | ||
| ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------- | | ||
| format | boolean | Apply formatting the the composed query. This will result in a multi-line soql statement. | FALSE | TRUE | | ||
| formatOptions | FormatOptions | Options to apply to the formatter. | FALSE | | | ||
| autoCompose | boolean | If you need to compose just part of a query, you can create your own instance of the Compose class and set this to false, then call any methods that you need to just for what you would like to turn into a SOQL query. | FALSE | TRUE | | ||
| logging | boolean | Print out logging statements to the console about the format operation. | FALSE | FALSE | | ||
**FormatOptions** | ||
| Property | Type | Description | required | default | | ||
| ---------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------- | | ||
| numIndent | number | The number of tab characters to indent. | FALSE | 1 | | ||
| fieldMaxLineLength | number | The number of characters that the fields should take up before making a new line. Set this to 1 to have every field on its own line. | FALSE | 60 | | ||
| fieldSubqueryParensOnOwnLine | boolean | If true, the opening and closing parentheses will be on their own line for subqueries. | FALSE | TRUE | | ||
| whereClauseOperatorsIndented | boolean | If true, indents the where clause operators | FALSE | FALSE | | ||
| logging | boolean | Print out logging statements to the console about the format operation. | FALSE | FALSE | | ||
## Examples | ||
### Parsing Queries | ||
Parsing a SOQL query can be completed by calling `parseQuery(soqlQueryString)`. A `Query` data structure will be returned. | ||
```typescript | ||
@@ -45,3 +80,3 @@ import { parseQuery } from 'soql-parser-js'; | ||
AND LoginTime < 2010-09-21T22:16:30.000Z | ||
GROUP BY UserId'; | ||
GROUP BY UserId | ||
`; | ||
@@ -54,3 +89,4 @@ | ||
**Results** | ||
<details> | ||
<summary><b>Results (click to show)</b></summary> | ||
@@ -66,6 +102,6 @@ ```json | ||
"type": "FieldFunctionExpression", | ||
"rawValue": "COUNT(Id)", | ||
"fn": "COUNT", | ||
"functionName": "COUNT", | ||
"parameters": ["Id"], | ||
"isAggregateFn": true, | ||
"parameters": ["Id"] | ||
"rawValue": "COUNT(Id)" | ||
} | ||
@@ -97,13 +133,21 @@ ], | ||
#### Options | ||
</details> | ||
### Validating Queries | ||
```typescript | ||
export interface SoqlQueryConfig { | ||
continueIfErrors?: boolean; // default=false | ||
logging: boolean; // default=false | ||
} | ||
import { isQueryValid } from 'soql-parser-js'; | ||
// var soqlParserJs = require('soql-parser-js'); // node's require format - usage: soqlParserJs.parseQuery() | ||
const invalidSoql = `SELECT UserId, COUNT(Id) Account`; | ||
const validSoql = `SELECT UserId, COUNT(Id) Account`; | ||
console.log(isQueryValid(soql)); | ||
console.log(isQueryValid(soql)); | ||
``` | ||
## Composing | ||
### Composing Queries | ||
Build a `Query` data structure to have it converted back into a SOQL query. | ||
Composing a query will turn a Query object back to a SOQL query string. The exact same data structure returned from `parseQuery()` can be used, | ||
@@ -113,51 +157,21 @@ but depending on your use-case, you may need to build your own data structure to compose a query. | ||
:page_facing_up: **Note:** Some operators may be converted to upper case (e.x. NOT, AND) | ||
Some utility methods have been provided to make it easier to build the field data structures. | ||
:page_facing_up: **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. | ||
**Note:** Some operators may be converted to upper case (e.x. NOT, AND) | ||
**The base query object is shaped like this:** | ||
**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. | ||
```typescript | ||
export interface QueryBase { | ||
fields: FieldType[]; | ||
sObjectAlias?: string; | ||
where?: WhereClause; | ||
limit?: number; | ||
offset?: number; | ||
groupBy?: GroupByClause; | ||
having?: HavingClause; | ||
orderBy?: OrderByClause | OrderByClause[]; | ||
withDataCategory?: WithDataCategoryClause; | ||
for?: ForClause; | ||
update?: UpdateClause; | ||
} | ||
``` | ||
import { composeQuery, getField, Query } from 'soql-parser-js'; | ||
The easiest way to build the fields is to call the utility function `getComposedField()`. | ||
### Example | ||
This is the query that will be composed programmatically | ||
```sql | ||
SELECT Id, Name, FORMAT(Amount) MyFormattedAmount, | ||
(SELECT Quantity, ListPrice, PricebookEntry.UnitPrice, PricebookEntry.Name FROM OpportunityLineItems) | ||
FROM Opportunity | ||
WHERE CreatedDate > LAST_N_YEARS:1 | ||
AND StageName = 'Closed Won' | ||
LIMIT 150 | ||
``` | ||
```typescript | ||
import { composeQuery, getComposedField } from 'soql-parser-js'; | ||
// Build a subquery | ||
const oppLineItemsSubquery = { | ||
fields: [ | ||
getComposedField('Quantity'), | ||
getComposedField('ListPrice'), | ||
getComposedField({ | ||
getField('Quantity'), | ||
getField('ListPrice'), | ||
getField({ | ||
field: 'UnitPrice', | ||
relationships: ['PricebookEntry'], | ||
}), | ||
getComposedField({ | ||
getField({ | ||
field: 'Name', | ||
@@ -170,12 +184,13 @@ relationships: ['PricebookEntry'], | ||
const soqlQuery = { | ||
// build the main query and add the subquery as a field | ||
const soqlQuery: Query = { | ||
fields: [ | ||
getComposedField('Id'), | ||
getComposedField('Name'), | ||
getComposedField({ | ||
fn: 'FORMAT', | ||
getField('Id'), | ||
getField('Name'), | ||
getField({ | ||
functionName: 'FORMAT', | ||
parameters: 'Amount', | ||
alias: 'MyFormattedAmount', | ||
}), | ||
getComposedField({ subquery: oppLineItemsSubquery }), | ||
getField({ subquery: oppLineItemsSubquery }), | ||
], | ||
@@ -209,32 +224,15 @@ sObject: 'Opportunity', | ||
In the example above, we made use of `getComposedField(input: string | ComposeFieldInput)` to compose the fields. The input expects a string or one of the following data structures listed below. An error will be thrown if the data passed in is not one of the following shapes. | ||
**Results** | ||
This returns a `FieldType` object. | ||
```typescript | ||
export interface ComposeField { | ||
field: string; | ||
objectPrefix?: string; | ||
} | ||
export interface ComposeFieldFunction { | ||
fn: string; | ||
parameters?: string | string[] | FieldFunctionExpression | FieldFunctionExpression[]; | ||
alias?: string; | ||
} | ||
export interface ComposeFieldRelationship { | ||
field: string; | ||
relationships: string[]; | ||
objectPrefix?: string; | ||
} | ||
export interface ComposeFieldSubquery { | ||
subquery?: Subquery; | ||
} | ||
export interface ComposeFieldTypeof { | ||
field: string; | ||
conditions: FieldTypeOfCondition[]; | ||
} | ||
```sql | ||
SELECT Id, Name, FORMAT(Amount) MyFormattedAmount, | ||
( | ||
SELECT Quantity, ListPrice, PricebookEntry.UnitPrice, | ||
PricebookEntry.Name | ||
FROM OpportunityLineItems | ||
) | ||
FROM Opportunity | ||
WHERE CreatedDate > LAST_N_YEARS:1 | ||
AND StageName = 'Closed Won' | ||
LIMIT 150 | ||
``` | ||
@@ -281,3 +279,2 @@ | ||
// Name = 'Foo' | ||
} | ||
@@ -287,26 +284,2 @@ | ||
## Checking if a Query is Valid | ||
This will parse a Query to confirm valid syntax, but will not parse into the Query data structure, which will have a small performance gain. | ||
This method is faster than parsing the full query. | ||
Options: | ||
```typescript | ||
export interface ConfigBase { | ||
logging: boolean; // default=false | ||
} | ||
``` | ||
```typescript | ||
import { isQueryValid } from 'soql-parser-js'; | ||
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'; | ||
const isValid = isQueryValid(soql); | ||
console.log(isValid); | ||
``` | ||
## Format Query | ||
@@ -324,3 +297,3 @@ | ||
const formattedQuery2 = formatQuery(query, { | ||
fieldMaxLineLen: 20, | ||
fieldMaxLineLength: 20, | ||
fieldSubqueryParensOnOwnLine: false, | ||
@@ -398,27 +371,2 @@ whereClauseOperatorsIndented: true, | ||
### Options | ||
```typescript | ||
export interface SoqlQueryConfig { | ||
continueIfErrors?: boolean; // default=false | ||
logging: boolean; // default=false | ||
includeSubqueryAsField: boolean; // default=true | ||
} | ||
export interface SoqlComposeConfig { | ||
logging: boolean; // default=false | ||
format: boolean; // default=false | ||
formatOptions?: FormatOptions; | ||
autoCompose: boolean; // default=true | ||
} | ||
export interface FormatOptions { | ||
numIndent?: number; // default=1 | ||
fieldMaxLineLen?: number; // default=60 | ||
fieldSubqueryParensOnOwnLine?: boolean; // default=true | ||
whereClauseOperatorsIndented?: boolean; // default=false | ||
logging?: boolean; // default=false | ||
} | ||
``` | ||
## Utility Functions | ||
@@ -428,3 +376,3 @@ | ||
1. `getComposedField(input: string | ComposeFieldInput)` | ||
1. `getField(input: string | ComposeFieldInput)` | ||
1. Convenience method to construct fields in the correct data format. See example usage in the Compose example. | ||
@@ -434,3 +382,3 @@ 2. `isSubquery(query: Query | Subquery)` | ||
3. `getFlattenedFields(query: Query)` | ||
1. Flatten a Salesforce record based on the parsed SOQL Query. this is useful if you have relationships in your query and want to show the results in a table, using `.` dot notation for the relationship fields. | ||
1. Flatten a Salesforce record based on the parsed SOQL Query. this is useful if you have relationships in your query and want to show the results in a table, using `.` dot notation for the relationship field headings. | ||
2. Refer to `tests/publicUtils.spec.ts` for usage examples. | ||
@@ -440,4 +388,2 @@ | ||
These are all available for import in your typescript projects | ||
### Query | ||
@@ -453,12 +399,3 @@ | ||
export type UpdateClause = 'TRACKING' | 'VIEWSTAT'; | ||
export type LiteralType = | ||
| 'STRING' | ||
| 'INTEGER' | ||
| 'DECIMAL' | ||
| 'BOOLEAN' | ||
| 'NULL' | ||
| 'DATETIME' | ||
| 'DATE' | ||
| 'DATE_LITERAL' | ||
| 'DATE_N_LITERAL'; | ||
export type LiteralType = 'STRING' | 'INTEGER' | 'DECIMAL' | 'BOOLEAN' | 'NULL' | 'DATETIME' | 'DATE' | 'DATE_LITERAL' | 'DATE_N_LITERAL'; | ||
export type FieldType = Field | FieldFunctionExpression | FieldRelationship | FieldSubquery | FieldTypeOf; | ||
@@ -468,3 +405,51 @@ export type OrderByCriterion = 'ASC' | 'DESC'; | ||
export type GroupByType = 'CUBE' | 'ROLLUP'; | ||
export type DateLiteral = | ||
| 'YESTERDAY' | ||
| 'TODAY' | ||
| 'TOMORROW' | ||
| 'LAST_WEEK' | ||
| 'THIS_WEEK' | ||
| 'NEXT_WEEK' | ||
| 'LAST_MONTH' | ||
| 'THIS_MONTH' | ||
| 'NEXT_MONTH' | ||
| 'LAST_90_DAYS' | ||
| 'NEXT_90_DAYS' | ||
| 'THIS_QUARTER' | ||
| 'LAST_QUARTER' | ||
| 'NEXT_QUARTER' | ||
| 'THIS_YEAR' | ||
| 'LAST_YEAR' | ||
| 'NEXT_YEAR' | ||
| 'THIS_FISCAL_QUARTER' | ||
| 'LAST_FISCAL_QUARTER' | ||
| 'NEXT_FISCAL_QUARTER' | ||
| 'THIS_FISCAL_YEAR' | ||
| 'LAST_FISCAL_YEAR' | ||
| 'NEXT_FISCAL_YEAR'; | ||
export type DateNLiteral = | ||
| 'YESTERDAY' | ||
| 'NEXT_N_DAYS' | ||
| 'LAST_N_DAYS' | ||
| 'N_DAYS_AGO' | ||
| 'NEXT_N_WEEKS' | ||
| 'LAST_N_WEEKS' | ||
| 'N_WEEKS_AGO' | ||
| 'NEXT_N_MONTHS' | ||
| 'LAST_N_MONTHS' | ||
| 'N_MONTHS_AGO' | ||
| 'NEXT_N_QUARTERS' | ||
| 'LAST_N_QUARTERS' | ||
| 'N_QUARTERS_AGO' | ||
| 'NEXT_N_YEARS' | ||
| 'LAST_N_YEARS' | ||
| 'N_YEARS_AGO' | ||
| 'NEXT_N_FISCAL_QUARTERS' | ||
| 'LAST_N_FISCAL_QUARTERS' | ||
| 'N_FISCAL_QUARTERS_AGO' | ||
| 'NEXT_N_FISCAL_YEARS' | ||
| 'LAST_N_FISCAL_YEARS' | ||
| 'N_FISCAL_YEARS_AGO'; | ||
export interface Field { | ||
@@ -474,2 +459,4 @@ type: 'Field'; | ||
objectPrefix?: string; // required if object is aliased | ||
rawValue?: string; // only included if objectPrefix is defined | ||
alias?: string; | ||
} | ||
@@ -479,4 +466,4 @@ | ||
type: 'FieldFunctionExpression'; | ||
fn: string; | ||
parameters?: string[] | FieldFunctionExpression[]; | ||
functionName: string; | ||
parameters?: (string | FieldFunctionExpression)[]; | ||
alias?: string; | ||
@@ -493,2 +480,3 @@ isAggregateFn?: boolean; // not required for compose, will be populated if SOQL is parsed | ||
rawValue?: string; // not required for compose, will be populated if SOQL is parsed with the raw value of the entire field | ||
alias?: string; | ||
} | ||
@@ -499,3 +487,2 @@ | ||
subquery: Subquery; | ||
from?: string; // not required for compose, will be populated if SOQL is parsed | ||
} | ||
@@ -522,5 +509,5 @@ | ||
groupBy?: GroupByClause; | ||
having?: HavingClause; | ||
orderBy?: OrderByClause | OrderByClause[]; | ||
withDataCategory?: WithDataCategoryClause; | ||
withSecurityEnforced?: boolean; | ||
for?: ForClause; | ||
@@ -554,3 +541,3 @@ update?: UpdateClause; | ||
valueQuery?: Query; | ||
literalType?: LiteralType; // If populated with STRING on compose, the value(s) will be wrapped in "'" if they are not already. - All other values ignored | ||
literalType?: LiteralType | LiteralType[]; // If populated with STRING on compose, the value(s) will be wrapped in "'" if they are not already. - All other values ignored | ||
dateLiteralVariable?: number; // not required for compose, will be populated if SOQL is parsed | ||
@@ -567,4 +554,5 @@ } | ||
export interface GroupByClause { | ||
field: string | string[]; | ||
type?: GroupByType; | ||
field?: string | string[]; | ||
fn?: FunctionExp; | ||
having?: HavingClause; | ||
} | ||
@@ -585,11 +573,11 @@ | ||
value: string | number; | ||
literalType?: String; | ||
} | ||
export interface FunctionExp { | ||
text?: string; // Should be formatted like this: Count(Id) | ||
name?: string; // not used for compose, will be populated if SOQL is parsed | ||
rawValue?: string; // Should be formatted like this: Count(Id) | ||
functionName?: string; // not used for compose, will be populated if SOQL is parsed | ||
alias?: string; | ||
parameter?: string | string[]; // not used for compose, will be populated if SOQL is parsed | ||
parameters?: (string | FunctionExp)[]; // not used for compose, will be populated if SOQL is parsed | ||
isAggregateFn?: boolean; // not used for compose, will be populated if SOQL is parsed | ||
fn?: FunctionExp; // used for nested functions FORMAT(MIN(CloseDate)) | ||
} | ||
@@ -632,3 +620,2 @@ | ||
parseWithDataCategory(withDataCategory: WithDataCategoryClause): string | ||
``` | ||
@@ -639,3 +626,5 @@ | ||
```typescript | ||
export interface ComposeField { | ||
type ComposeFieldInput = ComposeField | ComposeFieldFunction | ComposeFieldRelationship | ComposeFieldSubquery | ComposeFieldTypeof; | ||
interface ComposeField { | ||
field: string; | ||
@@ -645,3 +634,3 @@ objectPrefix?: string; | ||
export interface ComposeFieldFunction { | ||
interface ComposeFieldFunction { | ||
fn: string; | ||
@@ -652,3 +641,3 @@ parameters?: string | string[] | FieldFunctionExpression | FieldFunctionExpression[]; | ||
export interface ComposeFieldRelationship { | ||
interface ComposeFieldRelationship { | ||
field: string; | ||
@@ -659,7 +648,7 @@ relationships: string[]; | ||
export interface ComposeFieldSubquery { | ||
interface ComposeFieldSubquery { | ||
subquery?: Subquery; | ||
} | ||
export interface ComposeFieldTypeof { | ||
interface ComposeFieldTypeof { | ||
field: string; | ||
@@ -669,47 +658,10 @@ conditions: FieldTypeOfCondition[]; | ||
export type ComposeFieldInput = | ||
| ComposeField | ||
| ComposeFieldFunction | ||
| ComposeFieldRelationship | ||
| ComposeFieldSubquery | ||
| ComposeFieldTypeof; | ||
function isSubquery(query: Query | Subquery): query is Subquery; | ||
function getComposedField(input: string | ComposeFieldInput): SoqlModels.FieldType; | ||
function getField(input: string | ComposeFieldInput): SoqlModels.FieldType; | ||
function getFlattenedFields(query: SoqlModels.Query, isAggregateResult?: boolean): string[]; | ||
``` | ||
## CLI Usage | ||
The CLI can be used to parse a query or compose a previously parsed query back to SOQL. | ||
**Examples:** | ||
```shell | ||
$ npm install -g soql-parser-js | ||
$ soql --help | ||
$ soql --query "SELECT Id FROM Account" | ||
$ soql -query "SELECT Id FROM Account" | ||
$ soql -query "SELECT Id FROM Account" -output some-output-file.json | ||
$ soql -query "SELECT Id FROM Account" -json | ||
$ soql -query some-input-file.txt | ||
$ soql -compose some-input-file.json | ||
$ soql -compose some-input-file.json | ||
$ soql -compose some-input-file.json -output some-output-file.json | ||
``` | ||
**Arguments:** | ||
``` | ||
--query, -q A SOQL query surrounded in quotes or a file path to a text file containing a SOQL query. | ||
--compose, -c An escaped and quoted parsed SOQL JSON string or a file path to a text file containing a parsed query JSON object. | ||
--output, -o Filepath. | ||
--json, -j Provide all output messages as JSON. | ||
--debug, -d Print additional debug log messages. | ||
--help, -h Show this help message. | ||
``` | ||
## Contributing | ||
All contributions are welcome on the project. Please read the [contribution guidelines](https://github.com/paustint/soql-parser-js/blob/master/CONTRIBUTING.md). | ||
## Special Thanks | ||
- This library is based on the ANTLR4 grammar file [produced by Mulesoft](https://github.com/mulesoft/salesforce-soql-parser/blob/antlr4/SOQL.g4). | ||
- The following repository also was a help to get things started: https://github.com/petermetz/antlr-4-ts-test |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
2
0
667270
16
15
14308
2
637
+ Addedchevrotain@^6.5.0
+ Addedlodash.get@^4.4.2
+ Addedchevrotain@6.5.0(transitive)
+ Addedlodash.get@4.4.2(transitive)
+ Addedregexp-to-ast@0.4.0(transitive)
- Removed@types/lodash@^4.14.136
- Removedantlr4ts@0.4.1-alpha.0
- Removedlodash@^4.17.14
- Removedminimist@^1.2.0
- Removed@types/lodash@4.17.13(transitive)
- Removedantlr4ts@0.4.1-alpha.0(transitive)
- Removedlodash@4.17.21(transitive)
- Removedminimist@1.2.8(transitive)