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

wip: Import from SQL #1103

Draft
wants to merge 22 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
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/
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 @@ -548,9 +549,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 @@ -767,6 +770,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 @@ -775,7 +779,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 @@ -849,13 +853,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 @@ -931,7 +935,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 @@ -953,11 +957,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
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,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 @@ -523,6 +524,11 @@ 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`.

### Save/load config from server

#### `compressConfig`
Expand Down Expand Up @@ -668,15 +674,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.5",
"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