Skip to content

Commit

Permalink
Import from SQL (#1103)
Browse files Browse the repository at this point in the history
* wip

* .

* wip

* wip

* remove rsync

* use ConfigUtils for (de)compress

* isObject

* setIn, mergeIn

* fix mergeIn

* progr

* .

* deepCopyObj, arrayMergeMode, symbols

.

.

.

,

* .

* .

* IF()

* add sqlImport for funcs

* .

* .

* fix imports cycles

.

.

.

* wip sqlImport for ops

* .

* .

* fixes after merge

* example app: load from sql

* valueFuncs

* pre-defined sqlImportDate

* fix #1024

.

* linear

* fix ops like, not_like ; fix multiselect value type

* lint

* .

* .

* .

* pr

* .

* tableName

* .

* lint fix
  • Loading branch information
ukrbublik authored Jan 8, 2025
1 parent 5769193 commit a5c11dc
Show file tree
Hide file tree
Showing 114 changed files with 5,580 additions and 2,574 deletions.
1 change: 1 addition & 0 deletions .codesandbox/ci.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"packages": [
"packages/core",
"packages/sql",
"packages/ui",
"packages/antd",
"packages/mui",
Expand Down
148 changes: 86 additions & 62 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = {
"eslint:recommended",
"plugin:import/recommended",
// "plugin:import/typescript", // not needed for JS
"plugin:react/recommended",
// "plugin:react/recommended", // not needed for core
// "plugin:@typescript-eslint/eslint-recommended", // not needed for JS
],
"globals": {
Expand Down Expand Up @@ -134,73 +134,97 @@ module.exports = {
]
},
"overrides": [
{
"files": ["packages/tests/**/*"],
"env": {
"mocha": true,
// "jasmine": true,
{
"files": ["packages/sql/**/*"],
"settings": {
"import/resolver": {
"typescript": {
"project": [
"packages/sql/tsconfig.json",
],
},
},
},
},
"settings": {
"import/core-modules": [
"sinon",
"chai",
"mocha"
],
// "import/resolver": {
// "webpack": {
// "config": "./webpack.config.js"
// }
// },

{
"files": ["packages/tests/**/*"],
"env": {
"mocha": true,
// "jasmine": true,
},
"settings": {
"import/core-modules": [
"sinon",
"chai",
"mocha"
],
// "import/resolver": {
// "webpack": {
// "config": "./webpack.config.js"
// }
// },
},
},
},
{
"files": ["packages/sandbox_simple/**/*"],
"parser": "@babel/eslint-parser",
"parserOptions": {
"requireConfigFile": false,
"babelOptions": {
"presets": [
"@babel/preset-env",
"@babel/preset-react"

{
"files": ["packages/sandbox_simple/**/*"],
"parser": "@babel/eslint-parser",
"parserOptions": {
"requireConfigFile": false,
"babelOptions": {
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
},
"sourceType": "module",
},
"settings": {
"import/core-modules": [
"react",
"@react-awesome-query-builder/ui/css/styles.css"
],
},
"sourceType": "module",
},
"settings": {
"import/core-modules": [
"react",
"@react-awesome-query-builder/ui/css/styles.css"

{
"files": ["**/*.ts", "**/*.tsx"],
"extends": [
"eslint:recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],
"rules": {
"@typescript-eslint/no-unnecessary-type-assertion": 0,
//todo
"@typescript-eslint/no-unused-vars": 0,
"@typescript-eslint/ban-types": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-empty-interface": 0,
"@typescript-eslint/unbound-method": 0,
"@typescript-eslint/prefer-regexp-exec": 0,
"@typescript-eslint/no-empty-function": 0,
"@typescript-eslint/ban-ts-comment": 0,
"@typescript-eslint/no-floating-promises": 0,
"@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/no-non-null-asserted-optional-chain": 0,
}
},
{
"files": ["**/*.jsx", "**/*.tsx"],
"extends": [
"plugin:react/recommended",
],
"rules": {
//todo
"react/display-name": 0,
"react/prop-types": 0,
}
},
},

{
"files": ["**/*.ts", "**/*.tsx"],
"extends": [
"eslint:recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"plugin:react/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],
"rules": {
"@typescript-eslint/no-unnecessary-type-assertion": 0,
//todo
"@typescript-eslint/no-unused-vars": 0,
"@typescript-eslint/ban-types": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-empty-interface": 0,
"@typescript-eslint/unbound-method": 0,
"@typescript-eslint/prefer-regexp-exec": 0,
"@typescript-eslint/no-empty-function": 0,
"@typescript-eslint/ban-ts-comment": 0,
"@typescript-eslint/no-floating-promises": 0,
"@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/no-non-null-asserted-optional-chain": 0,
}
},
],
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ yarn.lock
packages/*/cjs/
packages/*/esm/
packages/*/css/
packages/*/types/
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Changelog
- 6.6.5
- Support import fom SQL (`SqlUtils.loadFromSql`) (PR #1103) (issue #911, #593)
- Fixed type issue with SerializedFunction (PR #1103) (issue #1024)
- 6.6.4
- Support groups inside rule-group (PR #1111) (issue #1108)
- Fixed cardinality issue (PR #1136) (issue #1118)
Expand Down
22 changes: 13 additions & 9 deletions CONFIG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ endif::[]
:renderSwitch: https://github.com/ukrbublik/react-awesome-query-builder/blob/master/packages/antd/modules/config/index.jsx#L54
:config_ser: https://github.com/ukrbublik/react-awesome-query-builder/blob/master/packages/sandbox_next/lib/config_ser.js
:d_ts: https://github.com/ukrbublik/react-awesome-query-builder/blob/master/packages/core/modules/index.d.ts
:ts-pattern: https://github.com/gvergnaud/ts-pattern

= Config format

Expand All @@ -37,7 +38,7 @@ Optionally you can override some options in basic config or add your own types/w
There are functions for building query string: `formatConj`, `formatValue`, `formatOp`, `formatField`, `formatFunc` which are used for `QbUtils.queryString()`. +
They have common param `isForDisplay` - false by default, true will be used for {queryString}[`QbUtils.queryString(immutableTree, config, true)`] (see 3rd param true). +
Also there are similar `mongoConj`, `mongoFormatOp`, `mongoFormatValue`, `mongoFunc`, `mongoFormatFunc`, `mongoArgsAsObject` for building MongoDb query with `QbUtils.mongodbFormat()`. +
And `sqlFormatConj`, `sqlOp`, `sqlFormatOp`, `sqlFormatValue`, `sqlFormatReverse`, `formatSpelField`, `sqlFunc`, `sqlFormatFunc` for building SQL where query with `QbUtils.sqlFormat()`. +
And `sqlFormatConj`, `sqlOp`, `sqlOps`, `sqlFormatOp`, `sqlFormatValue`, `sqlFormatReverse`, `formatSpelField`, `sqlFunc`, `sqlFormatFunc`, `sqlImport` for building SQL where query with `QbUtils.sqlFormat()`. +
And `spelFormatConj`, `spelOp`, `spelFormatOp`, `spelFormatValue`, `spelFormatReverse`, `spelFunc`, `spelFormatFunc` for building query in (https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/expressions.html)[Spring Expression Language (SpEL)] with `QbUtils.spelFormat()`. +
And `jsonLogic` for building http://jsonlogic.com[JsonLogic] with `QbUtils.jsonLogicFormat()`. +

Expand Down Expand Up @@ -347,7 +348,7 @@ Behaviour settings:
|removeEmptyGroupsOnLoad |true |Remove empty groups during initial validation of `value` prop passed to `<Query>`
|removeInvalidMultiSelectValuesOnLoad |true |Remove values that are not in `listValues` during initial validation of `value` prop passed to `<Query>`? +
By default `true`, but `false` for AntDesign as can be removed manually
|useConfigCompress |false |Set to `true` if you use `Utils.decompressConfig()`
|useConfigCompress |false |Set to `true` if you use `Utils.ConfigUtils.decompressConfig()`
|fieldItemKeysForSearch |`["label", "path", "altLabel", "grouplabel"]` |Keys in field item (see {d_ts}[type] `FieldItem`) available for search. Available keys: "key", "path", "label", "altLabel" (label2), "tooltip", "grouplabel" (label of parent group, for subfields of complex fields)
|listKeysForSearch |`["title", "value"]` |Keys in list item (see {d_ts}[type] `ListItem`) available for search. Available keys: "title", "value", "groupTitle"
|reverseOperatorsForNot |false |True to convert "!(x == 1)" to "x != 1" on import and export
Expand Down Expand Up @@ -549,9 +550,11 @@ where `AND` and `OR` - available conjuctions (logical operators). You can add `N
`value` - mixed for `cardinality==1` -or- `Array` for `cardinality>2` +
`useExpr` - true if resulted expression will be wrapped in https://docs.mongodb.com/manual/reference/operator/query/expr/index.html[`{'$expr': {...}}`] (used only if you compare field with another field or function) (you need to use aggregation operators in this case, like https://docs.mongodb.com/manual/reference/operator/aggregation/eq/[$eq (aggregation)] instead of https://docs.mongodb.com/manual/reference/operator/query/eq/[$eq])
|sqlOp |+ for SQL format | |Operator name in SQL
|sqlOps |- for SQL format | |Operator names in SQL
|sqlFormatOp |- for SQL format | |Function for advanced formatting SQL WHERE query when just `sqlOp` is not enough. +
`(string field, string op, mixed value, string valueSrc, string valueType, Object opDef, Object operatorOptions, Object fieldDef) => string` +
`value` - mixed for `cardinality==1` -or- `Array` for `cardinality>2`
|sqlImport |- for SQL format | |Function to convert given raw SQL value (not string, but object got from `node-sql-parser`) to `{ children: Array }`. If given expression can't be parsed into current operator, throw an error.
|spelOp |+ for SpEL format | |Operator name in SpEL
|spelFormatOp |- for SpEL format | |Function for advanced formatting query in SpEL when just `spelOp` is not enough. +
`(string field, string op, mixed value, string valueSrc, string valueType, Object opDef, Object operatorOptions, Object fieldDef) => string` +
Expand Down Expand Up @@ -768,6 +771,7 @@ To enable this feature set `valueSources` of type to `['value', 'func']` (see be
|sqlFunc |- for SQL format |same as func key |Func name in SQL
|sqlFormatFunc |- for SQL format | |Can be used instead of `sqlFunc`. Function with 1 param - args object `{<arg name> : <arg value>}`, should return formatted function expression string. +
Example: SUM function can be formatted with `({a, b}) => a + " + " + b`
|sqlImport |- for SQL format | |Function to convert given raw SQL value (not string, but object got from `node-sql-parser`) to `{args: Object}`. If given expression can't be parsed into current function, throw an error.
|spelFunc |- for SpEL format |same as func key |Func name in SpEL
|spelFormatFunc |- for SpEL format | |Can be used instead of `spelFunc`. Function with 1 param - args object `{<arg name> : <arg value>}`, should return formatted function expression string. +
Example: SUM function can be formatted with `({a, b}) => a + " + " + b`
Expand All @@ -776,7 +780,7 @@ To enable this feature set `valueSources` of type to `['value', 'func']` (see be
|mongoFormatFunc |- for MongoDB format | |Can be used instead of `mongoFunc`. Function with 1 param - args object `{<arg name> : <arg value>}`, should return formatted function expression object.
|jsonLogic |+ for http://jsonlogic.com[JsonLogic] | |String (function name) or function with 1 param - args object `{<arg name> : <arg value>}`, should return formatted function expression for JsonLogic.
|jsonLogicImport | | |Function to convert given JsonLogic expression to array of arguments of current function. If given expression can't be parsed into current function, throw an error.
|spelImport | | |Function to convert given raw SpEL value to array of arguments of current function. If given value can't be parsed into current function, throw an error.
|spelImport | | |Function to convert given raw SpEL value to object of arguments of current function. If given value can't be parsed into current function, throw an error or return undefined.
|args.* | | |Arguments of function. Config is almost same as for simple link:#configfields[fields]
|args.<arg>.label | |arg's key |Label to be displayed in arg's label or placeholder (if `config.settings.showLabels` is false)
|args.<arg>.type |+ | |One of types described in link:#configtypes[config.types]
Expand Down Expand Up @@ -850,13 +854,13 @@ const ctx = {
const zipConfig = {
fields,
settings: {
useConfigCompress: true, // this is required to use Utils.decompressConfig()
useConfigCompress: true, // this is required to use Utils.ConfigUtils.decompressConfig()
},
// you can add here other sections like `widgets` or `types`, but don't add `ctx`
};
// Config can be loaded from backend with providing `ctx`
const config = Utils.decompressConfig(zipConfig, BasicConfig, ctx);
const config = Utils.ConfigUtils.decompressConfig(zipConfig, BasicConfig, ctx);
----

You _can't_ just pass JS function to `validateValue` in `fieldSettings` because functions can't be serialized to JSON.
Expand Down Expand Up @@ -932,7 +936,7 @@ const zipConfig = {
To build zip config from full config you can use this util:
[source,javascript]
----
const zipConfig = Utils.compressConfig(config, BasicConfig);
const zipConfig = Utils.ConfigUtils.compressConfig(config, BasicConfig);
----
In order to generate zip config corretly (to JSON-serializable object), you should put your custom functions to `ctx` and refer to them by names as in examples above.
[source,javascript]
Expand All @@ -954,11 +958,11 @@ const config = merge({}, BasicConfig, {
},
ctx,
});
const zipConfig = Utils.compressConfig(config, BasicConfig);
const config2 = Utils.decompressConfig(zipConfig, BasicConfig, ctx); // should be same as `config`
const zipConfig = Utils.ConfigUtils.compressConfig(config, BasicConfig);
const config2 = Utils.ConfigUtils.decompressConfig(zipConfig, BasicConfig, ctx); // should be same as `config`
----

NOTE: `settings.useConfigCompress` should be `true` if you use `Utils.decompressConfig()`
NOTE: `settings.useConfigCompress` should be `true` if you use `Utils.ConfigUtils.decompressConfig()`


{nbsp} +
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Or with VSCode:
- [`stores`](/packages/core/modules/stores) - Tree store reducer
- [`actions`](/packages/core/modules/actions) - Actions to dispatch on store
- [`index.d.ts`](/packages/core/modules/index.d.ts) - TS definitions
- [`packages/sql/modules`](/packages/sql/modules) - SQL support
- [`packages/ui/modules`](/packages/ui/modules) - Core React components
- [`stores`](/packages/ui/modules/stores) - Tree store reducer for Redux (reused from `core`)
- [`actions`](/packages/ui/modules/actions) - Actions to dispatch on store (reused from `core`, added `drag`)
Expand Down
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ From v6 library is divided into packages:

```mermaid
graph LR;
core((core))-->ui(ui);
core-->ui;
core-->sql((sql));
ui-->antd;
ui-->mui;
ui-->material;
Expand Down Expand Up @@ -528,6 +529,12 @@ Wrapping in `div.query-builder-container` is necessary if you put query builder
`Utils.Import.loadFromSpel (string, config) -> [Immutable, errors]`
Convert query value from [Spring Expression Language (SpEL)](https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/expressions.html) format to internal Immutable format.

#### `loadFromSql`
`SqlUtils.loadFromSql (string, config) -> {tree: Immutable, errors: string[]}`
Convert query value from SQL format to internal Immutable format.
Requires import of `@react-awesome-query-builder/sql`:
`import { SqlUtils } from "@react-awesome-query-builder/sql"`

### Save/load config from server

#### `compressConfig`
Expand Down Expand Up @@ -673,15 +680,15 @@ See [example](/packages/examples/src/demo_switch/index.tsx)

## SSR
You can save and load config from server with help of utils:
- [Utils.compressConfig()](#compressconfig)
- [Utils.decompressConfig()](#decompressconfig)
- [Utils.ConfigUtils.compressConfig()](#compressconfig)
- [Utils.ConfigUtils.decompressConfig()](#decompressconfig)

You need these utils because you can't just send config *as-is* to server, as it contains functions that can't be serialized to JSON.
Note that you need to set `config.settings.useConfigCompress = true` to enable this feature.

To put it simple:
- `ZipConfig` type is a JSON that contains only changes against basic config (differences). At minimum it contains your `fields`. It does not contain [`ctx`](#ctx).
- `Utils.decompressConfig()` will merge `ZipConfig` to basic config (and add `ctx` if passed).
- `Utils.ConfigUtils.decompressConfig()` will merge `ZipConfig` to basic config (and add `ctx` if passed).

See [sandbox_next demo app](/packages/sandbox_next) that demonstrates server-side features.

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"author": "Denis Oblogin <[email protected]> (https://github.com/ukrbublik)",
"workspaces": [
"packages/core",
"packages/sql",
"packages/ui",
"packages/antd",
"packages/mui",
Expand Down Expand Up @@ -111,6 +112,7 @@
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-react": "^7.34.1",
"lerna": "^8.1.9",
"madge": "^8.0.0",
"typescript": "~5.4.5"
},
"engines": {
Expand Down
5 changes: 2 additions & 3 deletions packages/antd/modules/utils/stuff.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Utils } from "@react-awesome-query-builder/ui";
const { getItemInListValues, listValuesToArray } = Utils.ListUtils;
const { isObjectOrArray } = Utils.OtherUtils;


const isObject = (v) => (typeof v == "object" && v !== null); // object or array

export const defaultTreeDataMap = {id: "value", pId: "parent", rootPId: undefined};

// converts from treeData to treeDataSimpleMode format (https://ant.design/components/tree-select/)
Expand Down Expand Up @@ -39,7 +38,7 @@ const flatizeTreeData = (treeData) => {
len = treeData.length;
for (rind = 0 ; rind < len ; rind++) {
const c = treeData[rind];
if (!isObject(c))
if (!isObjectOrArray(c))
continue;
if (c[tdm.pId] !== undefined && c[tdm.pId] != tdm.rootPId)
continue; //not lev 1
Expand Down
10 changes: 8 additions & 2 deletions packages/antd/scripts/build-npm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ rm -rf ./cjs
rm -rf ./esm

babel --extensions ".tsx,.jsx,.ts,.js" -d ./cjs ./modules
find cjs/ -type f -name "*.d.js" | xargs -I{} rm {}
#find ./cjs -name "*.js" -exec sed -i.bak "s+antd/es/+antd/lib/+g" {} +
#rm ./cjs/**/*.bak
node ./scripts/fix-antd.js

ESM=1 babel --extensions ".tsx,.jsx,.ts,.js" -d ./esm ./modules
cp ./modules/index.d.ts ./cjs/index.d.ts
cp ./modules/index.d.ts ./esm/index.d.ts
find esm/ -type f -name "*.d.js" | xargs -I{} rm {}

# rsync -ma --include '*/' --include '*.d.ts' --exclude '*' ./modules/ ./cjs/
# rsync -ma --include '*/' --include '*.d.ts' --exclude '*' ./modules/ ./esm/
find modules/ -type f -name "*.d.ts" | cut -d'/' -f2- | xargs -I{} cp modules/{} cjs/{}
find modules/ -type f -name "*.d.ts" | cut -d'/' -f2- | xargs -I{} cp modules/{} esm/{}

rm -rf ./css
sass -I node_modules -I ../../node_modules styles/:css/ --no-source-map --style=expanded
Expand Down
2 changes: 1 addition & 1 deletion packages/antd/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"module": "esnext",
"jsx": "preserve",
"lib": [
"es2015",
"es2016",
"dom",
"dom.iterable"
],
Expand Down
Loading

0 comments on commit a5c11dc

Please sign in to comment.