From d483ed121ca0fc5b90eac65f5e39cd6cc9359331 Mon Sep 17 00:00:00 2001 From: John Bodley <4567245+john-bodley@users.noreply.github.com> Date: Mon, 18 Jun 2018 15:43:18 -0700 Subject: [PATCH] [adhoc-filters] Adding adhoc-filters to all viz types (#5206) --- babel-node | 0 package-lock.json | 743 ++++++++++++++++++ superset/assets/backendSync.json | 24 - .../components/AlteredSliceTag_spec.jsx | 80 +- .../components/AdhocFilterControl_spec.jsx | 52 -- .../ControlPanelsContainer_spec.jsx | 2 +- .../explore/components/FilterControl_spec.jsx | 248 ------ .../explore/components/Filter_spec.jsx | 115 --- .../assets/src/components/AlteredSliceTag.jsx | 12 +- .../controls/AdhocFilterControl.jsx | 78 +- .../explore/components/controls/Filter.jsx | 187 ----- .../components/controls/FilterControl.jsx | 155 ---- .../src/explore/components/controls/index.js | 2 - superset/assets/src/explore/controls.jsx | 50 -- superset/assets/src/explore/store.js | 8 - superset/assets/src/explore/visTypes.js | 128 ++- .../versions/bddc498dd179_adhoc_filters.py | 97 +++ .../translations/de/LC_MESSAGES/messages.po | 1 - .../translations/en/LC_MESSAGES/messages.po | 1 - .../translations/es/LC_MESSAGES/messages.po | 1 - .../translations/fr/LC_MESSAGES/messages.po | 1 - .../translations/it/LC_MESSAGES/messages.po | 1 - .../translations/ja/LC_MESSAGES/messages.po | 1 - superset/translations/messages.pot | 1 - .../pt_BR/LC_MESSAGES/messages.po | 1 - .../translations/ru/LC_MESSAGES/messages.po | 1 - .../translations/zh/LC_MESSAGES/messages.po | 1 - superset/utils.py | 1 - 28 files changed, 1012 insertions(+), 980 deletions(-) create mode 100644 babel-node create mode 100644 package-lock.json delete mode 100644 superset/assets/spec/javascripts/explore/components/FilterControl_spec.jsx delete mode 100644 superset/assets/spec/javascripts/explore/components/Filter_spec.jsx delete mode 100644 superset/assets/src/explore/components/controls/Filter.jsx delete mode 100644 superset/assets/src/explore/components/controls/FilterControl.jsx create mode 100644 superset/migrations/versions/bddc498dd179_adhoc_filters.py diff --git a/babel-node b/babel-node new file mode 100644 index 000000000..e69de29bb diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..dea4bad05 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,743 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-1.2.0.tgz", + "integrity": "sha512-yeDwKaLgGdTpXL7RgGt5r6T4LmnTza/hUn5Ul8uZSGGMtEjYo13Nxai7SQaGCTEzUtg9Zq9qJn0EjEr7SeSlTQ==", + "requires": { + "babel-plugin-syntax-dynamic-import": "^6.18.0" + } + }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "babel-plugin-syntax-dynamic-import": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=" + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "dev": true, + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-env": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", + "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.23.0", + "babel-plugin-transform-es2015-classes": "^6.23.0", + "babel-plugin-transform-es2015-computed-properties": "^6.22.0", + "babel-plugin-transform-es2015-destructuring": "^6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", + "babel-plugin-transform-es2015-for-of": "^6.23.0", + "babel-plugin-transform-es2015-function-name": "^6.22.0", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-umd": "^6.23.0", + "babel-plugin-transform-es2015-object-super": "^6.22.0", + "babel-plugin-transform-es2015-parameters": "^6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", + "babel-plugin-transform-exponentiation-operator": "^6.22.0", + "babel-plugin-transform-regenerator": "^6.22.0", + "browserslist": "^3.2.6", + "invariant": "^2.2.2", + "semver": "^5.3.0" + } + }, + "babel-preset-es2015": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", + "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + } + }, + "caniuse-lite": { + "version": "1.0.30000856", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000856.tgz", + "integrity": "sha512-x3mYcApHMQemyaHuH/RyqtKCGIYTgEA63fdi+VBvDz8xUSmRiVWTLeyKcoGQCGG6UPR9/+4qG4OKrTa6aSQRKg==", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "core-js": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "electron-to-chromium": { + "version": "1.3.48", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.48.tgz", + "integrity": "sha1-07DYWTgUBE4JLs4hCPw6ya6kuQA=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "requires": { + "js-tokens": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==" + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "requires": { + "jsesc": "~0.5.0" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" + } + } +} diff --git a/superset/assets/backendSync.json b/superset/assets/backendSync.json index 9145cc981..9b825529e 100644 --- a/superset/assets/backendSync.json +++ b/superset/assets/backendSync.json @@ -1683,18 +1683,6 @@ "renderTrigger": true, "default": "" }, - "where": { - "type": "TextControl", - "label": "Custom WHERE clause", - "default": "", - "description": "The text in this box gets included in your query's WHERE clause, as an AND to other criteria. You can include complex expression, parenthesis and anything else supported by the backend it is directed towards." - }, - "having": { - "type": "TextControl", - "label": "Custom HAVING clause", - "default": "", - "description": "The text in this box gets included in your query's HAVING clause, as an AND to other criteria. You can include complex expression, parenthesis and anything else supported by the backend it is directed towards." - }, "compare_lag": { "type": "TextControl", "label": "Comparison Period Lag", @@ -2628,12 +2616,6 @@ "default": "", "description": "Labels for the marker lines" }, - "filters": { - "type": "FilterControl", - "label": "", - "default": [], - "description": "" - }, "annotation_layers": { "type": "AnnotationLayerControl", "label": "", @@ -2641,12 +2623,6 @@ "description": "Annotation Layers", "renderTrigger": true }, - "having_filters": { - "type": "FilterControl", - "label": "", - "default": [], - "description": "" - }, "slice_id": { "type": "HiddenControl", "label": "Slice ID", diff --git a/superset/assets/spec/javascripts/components/AlteredSliceTag_spec.jsx b/superset/assets/spec/javascripts/components/AlteredSliceTag_spec.jsx index 79947201e..867006993 100644 --- a/superset/assets/spec/javascripts/components/AlteredSliceTag_spec.jsx +++ b/superset/assets/spec/javascripts/components/AlteredSliceTag_spec.jsx @@ -11,7 +11,15 @@ import TooltipWrapper from '../../../src/components/TooltipWrapper'; const defaultProps = { origFormData: { - filters: [{ col: 'a', op: '==', val: 'hello' }], + adhoc_filters: [ + { + clause: 'WHERE', + comparator: 'hello', + expressionType: 'SIMPLE', + operator: '==', + subject: 'a', + }, + ], y_axis_bounds: [10, 20], column_collection: [{ 1: 'a', b: ['6', 'g'] }], bool: false, @@ -21,7 +29,15 @@ const defaultProps = { ever: { a: 'b', c: 'd' }, }, currentFormData: { - filters: [{ col: 'b', op: 'in', val: ['hello', 'my', 'name'] }], + adhoc_filters: [ + { + clause: 'WHERE', + comparator: ['hello', 'my', 'name'], + expressionType: 'SIMPLE', + operator: 'in', + subject: 'b', + }, + ], y_axis_bounds: [15, 16], column_collection: [{ 1: 'a', b: [9, '15'], t: 'gggg' }], bool: true, @@ -33,9 +49,25 @@ const defaultProps = { }; const expectedDiffs = { - filters: { - before: [{ col: 'a', op: '==', val: 'hello' }], - after: [{ col: 'b', op: 'in', val: ['hello', 'my', 'name'] }], + adhoc_filters: { + before: [ + { + clause: 'WHERE', + comparator: 'hello', + expressionType: 'SIMPLE', + operator: '==', + subject: 'a', + }, + ], + after: [ + { + clause: 'WHERE', + comparator: ['hello', 'my', 'name'], + expressionType: 'SIMPLE', + operator: 'in', + subject: 'b', + }, + ], }, y_axis_bounds: { before: [10, 20], @@ -211,25 +243,49 @@ describe('AlteredSliceTag', () => { }); it('returns "[]" for empty filters', () => { - expect(wrapper.instance().formatValue([], 'filters')).to.equal('[]'); + expect(wrapper.instance().formatValue([], 'adhoc_filters')).to.equal('[]'); }); it('correctly formats filters with array values', () => { const filters = [ - { col: 'a', op: 'in', val: ['1', 'g', '7', 'ho'] }, - { col: 'b', op: 'not in', val: ['hu', 'ho', 'ha'] }, + { + clause: 'WHERE', + comparator: ['1', 'g', '7', 'ho'], + expressionType: 'SIMPLE', + operator: 'in', + subject: 'a', + }, + { + clause: 'WHERE', + comparator: ['hu', 'ho', 'ha'], + expressionType: 'SIMPLE', + operator: 'not in', + subject: 'b', + }, ]; const expected = 'a in [1, g, 7, ho], b not in [hu, ho, ha]'; - expect(wrapper.instance().formatValue(filters, 'filters')).to.equal(expected); + expect(wrapper.instance().formatValue(filters, 'adhoc_filters')).to.equal(expected); }); it('correctly formats filters with string values', () => { const filters = [ - { col: 'a', op: '==', val: 'gucci' }, - { col: 'b', op: 'LIKE', val: 'moshi moshi' }, + { + clause: 'WHERE', + comparator: 'gucci', + expressionType: 'SIMPLE', + operator: '==', + subject: 'a', + }, + { + clause: 'WHERE', + comparator: 'moshi moshi', + expressionType: 'SIMPLE', + operator: 'LIKE', + subject: 'b', + }, ]; const expected = 'a == gucci, b LIKE moshi moshi'; - expect(wrapper.instance().formatValue(filters, 'filters')).to.equal(expected); + expect(wrapper.instance().formatValue(filters, 'adhoc_filters')).to.equal(expected); }); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/AdhocFilterControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/AdhocFilterControl_spec.jsx index 4be8a2eba..2123ed7c2 100644 --- a/superset/assets/spec/javascripts/explore/components/AdhocFilterControl_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/AdhocFilterControl_spec.jsx @@ -33,18 +33,9 @@ const columns = [ { type: 'DOUBLE', column_name: 'value' }, ]; -const legacyFilter = { col: 'value', op: '>', val: '5' }; -const legacyHavingFilter = { col: 'SUM(value)', op: '>', val: '10' }; -const whereFilterText = 'target in (\'alpha\')'; -const havingFilterText = 'SUM(value) < 20'; - const formData = { - filters: [legacyFilter], - having: havingFilterText, - having_filters: [legacyHavingFilter], metric: undefined, metrics: [sumValueAdhocMetric, savedMetric.saved_metric_name], - where: whereFilterText, }; function setup(overrides) { @@ -68,49 +59,6 @@ describe('AdhocFilterControl', () => { expect(wrapper.find(OnPasteSelect)).to.have.lengthOf(1); }); - it('will translate legacy filters into adhoc filters if no adhoc filters are present', () => { - const { wrapper } = setup({ value: undefined }); - expect(wrapper.state('values')).to.have.lengthOf(4); - expect(wrapper.state('values')[0].equals(( - new AdhocFilter({ - expressionType: EXPRESSION_TYPES.SIMPLE, - subject: 'value', - operator: '>', - comparator: '5', - clause: CLAUSES.WHERE, - }) - ))).to.be.true; - expect(wrapper.state('values')[1].equals(( - new AdhocFilter({ - expressionType: EXPRESSION_TYPES.SIMPLE, - subject: 'SUM(value)', - operator: '>', - comparator: '10', - clause: CLAUSES.HAVING, - }) - ))).to.be.true; - expect(wrapper.state('values')[2].equals(( - new AdhocFilter({ - expressionType: EXPRESSION_TYPES.SQL, - sqlExpression: 'target in (\'alpha\')', - clause: CLAUSES.WHERE, - }) - ))).to.be.true; - expect(wrapper.state('values')[3].equals(( - new AdhocFilter({ - expressionType: EXPRESSION_TYPES.SQL, - sqlExpression: 'SUM(value) < 20', - clause: CLAUSES.HAVING, - }) - ))).to.be.true; - }); - - it('will ignore legacy filters if adhoc filters are present', () => { - const { wrapper } = setup(); - expect(wrapper.state('values')).to.have.lengthOf(1); - expect(wrapper.state('values')[0]).to.equal(simpleAdhocFilter); - }); - it('handles saved metrics being selected to filter on', () => { const { wrapper, onChange } = setup({ value: [] }); const select = wrapper.find(OnPasteSelect); diff --git a/superset/assets/spec/javascripts/explore/components/ControlPanelsContainer_spec.jsx b/superset/assets/spec/javascripts/explore/components/ControlPanelsContainer_spec.jsx index fb1ea610f..64a657e1f 100644 --- a/superset/assets/spec/javascripts/explore/components/ControlPanelsContainer_spec.jsx +++ b/superset/assets/spec/javascripts/explore/components/ControlPanelsContainer_spec.jsx @@ -26,6 +26,6 @@ describe('ControlPanelsContainer', () => { }); it('renders ControlPanelSections', () => { - expect(wrapper.find(ControlPanelSection)).to.have.lengthOf(7); + expect(wrapper.find(ControlPanelSection)).to.have.lengthOf(6); }); }); diff --git a/superset/assets/spec/javascripts/explore/components/FilterControl_spec.jsx b/superset/assets/spec/javascripts/explore/components/FilterControl_spec.jsx deleted file mode 100644 index 2b83fff45..000000000 --- a/superset/assets/spec/javascripts/explore/components/FilterControl_spec.jsx +++ /dev/null @@ -1,248 +0,0 @@ -/* eslint-disable no-unused-expressions */ -import React from 'react'; -import { Button } from 'react-bootstrap'; -import sinon from 'sinon'; -import { expect } from 'chai'; -import { describe, it, beforeEach } from 'mocha'; -import { shallow } from 'enzyme'; -import FilterControl from '../../../../src/explore/components/controls/FilterControl'; -import Filter from '../../../../src/explore/components/controls/Filter'; - -const $ = window.$ = require('jquery'); - -const defaultProps = { - name: 'not_having_filters', - onChange: sinon.spy(), - value: [ - { - col: 'col1', - op: 'in', - val: ['a', 'b', 'd'], - }, - { - col: 'col2', - op: '==', - val: 'Z', - }, - ], - datasource: { - id: 1, - type: 'qtable', - filter_select: true, - filterable_cols: [['col1', 'col2']], - metrics_combo: [ - ['m1', 'v1'], - ['m2', 'v2'], - ], - }, -}; - -describe('FilterControl', () => { - let wrapper; - - beforeEach(() => { - wrapper = shallow(); - wrapper.setState({ - filters: [ - { - valuesLoading: false, - valueChoices: ['a', 'b', 'c', 'd', 'e', 'f'], - }, - { - valuesLoading: false, - valueChoices: ['X', 'Y', 'Z'], - }, - // Need a duplicate since onChange calls are not changing props - { - valuesLoading: false, - valueChoices: ['X', 'Y', 'Z'], - }, - ], - }); - }); - - it('renders Filters', () => { - expect( - React.isValidElement(), - ).to.equal(true); - }); - - it('renders one button and two filters', () => { - expect(wrapper.find(Filter)).to.have.lengthOf(2); - expect(wrapper.find(Button)).to.have.lengthOf(1); - }); - - it('adds filter when clicking Add Filter', () => { - const addButton = wrapper.find('#add-button'); - expect(addButton).to.have.lengthOf(1); - addButton.simulate('click'); - expect(defaultProps.onChange).to.have.property('callCount', 1); - expect(defaultProps.onChange.getCall(0).args[0]).to.deep.equal([ - { - col: 'col1', - op: 'in', - val: ['a', 'b', 'd'], - }, - { - col: 'col2', - op: '==', - val: 'Z', - }, - { - col: 'col1', - op: 'in', - val: [], - }, - ]); - }); - - it('removes a the second filter when its delete button is clicked', () => { - expect(wrapper.find(Filter)).to.have.lengthOf(2); - wrapper.instance().removeFilter(1); - expect(defaultProps.onChange).to.have.property('callCount', 2); - expect(defaultProps.onChange.getCall(1).args[0]).to.deep.equal([ - { - col: 'col1', - op: 'in', - val: ['a', 'b', 'd'], - }, - ]); - }); - - before(() => { - sinon.stub($, 'ajax'); - }); - - after(() => { - $.ajax.restore(); - }); - - it('makes a GET request to retrieve value choices', () => { - wrapper.instance().fetchFilterValues(0, 'col1'); - expect($.ajax.getCall(0).args[0].type).to.deep.equal('GET'); - expect($.ajax.getCall(0).args[0].url).to.deep.equal('/superset/filter/qtable/1/col1/'); - }); - - it('changes filter values when one is removed', () => { - wrapper.instance().changeFilter(0, 'val', ['a', 'b']); - expect(defaultProps.onChange).to.have.property('callCount', 3); - expect(defaultProps.onChange.getCall(2).args[0]).to.deep.equal([ - { - col: 'col1', - op: 'in', - val: ['a', 'b'], - }, - { - col: 'col2', - op: '==', - val: 'Z', - }, - ]); - }); - - it('changes filter values when one is added', () => { - wrapper.instance().changeFilter(0, 'val', ['a', 'b', 'd', 'e']); - expect(defaultProps.onChange).to.have.property('callCount', 4); - expect(defaultProps.onChange.getCall(3).args[0]).to.deep.equal([ - { - col: 'col1', - op: 'in', - val: ['a', 'b', 'd', 'e'], - }, - { - col: 'col2', - op: '==', - val: 'Z', - }, - ]); - }); - - it('changes op and transforms values', () => { - wrapper.instance().changeFilter(0, ['val', 'op'], ['a', '==']); - wrapper.instance().changeFilter(1, ['val', 'op'], [['Z'], 'in']); - expect(defaultProps.onChange).to.have.property('callCount', 6); - expect(defaultProps.onChange.getCall(4).args[0]).to.deep.equal([ - { - col: 'col1', - op: '==', - val: 'a', - }, - { - col: 'col2', - op: '==', - val: 'Z', - }, - ]); - expect(defaultProps.onChange.getCall(5).args[0]).to.deep.equal([ - { - col: 'col1', - op: 'in', - val: ['a', 'b', 'd'], - }, - { - col: 'col2', - op: 'in', - val: ['Z'], - }, - ]); - }); - - it('changes column and clears invalid values', () => { - wrapper.instance().changeFilter(0, 'col', 'col2'); - expect(defaultProps.onChange).to.have.property('callCount', 7); - expect(defaultProps.onChange.getCall(6).args[0]).to.deep.equal([ - { - col: 'col2', - op: 'in', - val: [], - }, - { - col: 'col2', - op: '==', - val: 'Z', - }, - ]); - wrapper.instance().changeFilter(1, 'col', 'col1'); - expect(defaultProps.onChange).to.have.property('callCount', 8); - expect(defaultProps.onChange.getCall(7).args[0]).to.deep.equal([ - { - col: 'col1', - op: 'in', - val: ['a', 'b', 'd'], - }, - { - col: 'col1', - op: '==', - val: '', - }, - ]); - }); - - it('tracks an active filter select ajax request', () => { - const spyReq = sinon.spy(); - $.ajax.reset(); - $.ajax.onFirstCall().returns(spyReq); - wrapper.instance().fetchFilterValues(0, 'col1'); - expect(wrapper.state().activeRequest).to.equal(spyReq); - // Sets active to null after success - $.ajax.getCall(0).args[0].success(['opt1', 'opt2', null, '']); - expect(wrapper.state().filters[0].valuesLoading).to.equal(false); - expect(wrapper.state().filters[0].valueChoices).to.deep.equal(['opt1', 'opt2', null, '']); - expect(wrapper.state().activeRequest).to.equal(null); - }); - - - it('cancels active request if another is submitted', () => { - const spyReq = sinon.spy(); - spyReq.abort = sinon.spy(); - $.ajax.reset(); - $.ajax.onFirstCall().returns(spyReq); - wrapper.instance().fetchFilterValues(0, 'col1'); - expect(wrapper.state().activeRequest).to.equal(spyReq); - const spyReq1 = sinon.spy(); - $.ajax.onSecondCall().returns(spyReq1); - wrapper.instance().fetchFilterValues(1, 'col2'); - expect(spyReq.abort.called).to.equal(true); - expect(wrapper.state().activeRequest).to.equal(spyReq1); - }); -}); diff --git a/superset/assets/spec/javascripts/explore/components/Filter_spec.jsx b/superset/assets/spec/javascripts/explore/components/Filter_spec.jsx deleted file mode 100644 index 27425de9a..000000000 --- a/superset/assets/spec/javascripts/explore/components/Filter_spec.jsx +++ /dev/null @@ -1,115 +0,0 @@ -/* eslint-disable no-unused-expressions */ -import React from 'react'; -import Select from 'react-select'; -import { Button } from 'react-bootstrap'; -import sinon from 'sinon'; -import { expect } from 'chai'; -import { describe, it, beforeEach } from 'mocha'; -import { shallow } from 'enzyme'; -import Filter from '../../../../src/explore/components/controls/Filter'; -import SelectControl from '../../../../src/explore/components/controls/SelectControl'; - -const defaultProps = { - changeFilter: sinon.spy(), - removeFilter: () => {}, - filter: { - col: null, - op: 'in', - value: ['val'], - }, - datasource: { - id: 1, - type: 'qtable', - filter_select: false, - filterable_cols: ['col1', 'col2'], - metrics_combo: [ - ['m1', 'v1'], - ['m2', 'v2'], - ], - }, -}; - -describe('Filter', () => { - let wrapper; - - beforeEach(() => { - wrapper = shallow(); - }); - - it('renders Filters', () => { - expect( - React.isValidElement(), - ).to.equal(true); - }); - - it('renders two selects, one button and one input', () => { - expect(wrapper.find(Select)).to.have.lengthOf(2); - expect(wrapper.find(Button)).to.have.lengthOf(1); - expect(wrapper.find(SelectControl)).to.have.lengthOf(1); - expect(wrapper.find('#select-op').prop('options')).to.have.lengthOf(10); - }); - - it('renders five op choices for table datasource', () => { - const props = Object.assign({}, defaultProps); - props.datasource = { - id: 1, - type: 'druid', - filter_select: false, - filterable_cols: ['country_name'], - }; - const druidWrapper = shallow(); - expect(druidWrapper.find('#select-op').prop('options')).to.have.lengthOf(11); - }); - - it('renders six op choices for having filter', () => { - const props = Object.assign({}, defaultProps); - props.having = true; - const havingWrapper = shallow(); - expect(havingWrapper.find('#select-op').prop('options')).to.have.lengthOf(6); - }); - - it('calls changeFilter when select is changed', () => { - const selectCol = wrapper.find('#select-col'); - selectCol.simulate('change', { value: 'col' }); - const selectOp = wrapper.find('#select-op'); - selectOp.simulate('change', { value: 'in' }); - const selectVal = wrapper.find(SelectControl); - selectVal.simulate('change', { value: 'x' }); - expect(defaultProps.changeFilter).to.have.property('callCount', 3); - }); - - it('renders input for regex filters', () => { - const props = Object.assign({}, defaultProps); - props.filter = { - col: null, - op: 'regex', - value: 'val', - }; - const regexWrapper = shallow(); - expect(regexWrapper.find('input')).to.have.lengthOf(1); - }); - - it('renders `input` for text filters', () => { - const props = Object.assign({}, defaultProps); - ['>=', '>', '<=', '<'].forEach((op) => { - props.filter = { - col: 'col1', - op, - value: 'val', - }; - wrapper = shallow(); - expect(wrapper.find('input')).to.have.lengthOf(1); - }); - }); - - it('replaces null filter values with empty string in `input`', () => { - const props = Object.assign({}, defaultProps); - props.filter = { - col: 'col1', - op: '>=', - value: null, - }; - wrapper = shallow(); - expect(wrapper.find('input').props().value).to.equal(''); - }); -}); diff --git a/superset/assets/src/components/AlteredSliceTag.jsx b/superset/assets/src/components/AlteredSliceTag.jsx index 209e4a94b..a317a041a 100644 --- a/superset/assets/src/components/AlteredSliceTag.jsx +++ b/superset/assets/src/components/AlteredSliceTag.jsx @@ -42,6 +42,10 @@ export default class AlteredSliceTag extends React.Component { if (!ofd[fdKey] && !cfd[fdKey]) { continue; } + // Ignore obsolete legacy filters + if (['filters', 'having', 'having_filters', 'where'].includes(fdKey)) { + continue; + } if (!isEqual(ofd[fdKey], cfd[fdKey])) { diffs[fdKey] = { before: ofd[fdKey], after: cfd[fdKey] }; } @@ -56,13 +60,15 @@ export default class AlteredSliceTag extends React.Component { return 'N/A'; } else if (value === null) { return 'null'; - } else if (controls[key] && controls[key].type === 'FilterControl') { + } else if (controls[key] && controls[key].type === 'AdhocFilterControl') { if (!value.length) { return '[]'; } return value.map((v) => { - const filterVal = v.val && v.val.constructor === Array ? `[${v.val.join(', ')}]` : v.val; - return `${v.col} ${v.op} ${filterVal}`; + const filterVal = v.comparator && v.comparator.constructor === Array ? + `[${v.comparator.join(', ')}]` : + v.comparator; + return `${v.subject} ${v.operator} ${filterVal}`; }).join(', '); } else if (controls[key] && controls[key].type === 'BoundsControl') { return `Min: ${value[0]}, Max: ${value[1]}`; diff --git a/superset/assets/src/explore/components/controls/AdhocFilterControl.jsx b/superset/assets/src/explore/components/controls/AdhocFilterControl.jsx index 3a12c2a7b..ca0d501c0 100644 --- a/superset/assets/src/explore/components/controls/AdhocFilterControl.jsx +++ b/superset/assets/src/explore/components/controls/AdhocFilterControl.jsx @@ -16,12 +16,6 @@ import OnPasteSelect from '../../../components/OnPasteSelect'; import AdhocFilterOption from '../AdhocFilterOption'; import FilterDefinitionOption from '../FilterDefinitionOption'; -const legacyFilterShape = PropTypes.shape({ - col: PropTypes.string, - op: PropTypes.string, - val: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]), -}); - const propTypes = { name: PropTypes.string, onChange: PropTypes.func, @@ -30,12 +24,8 @@ const propTypes = { columns: PropTypes.arrayOf(columnType), savedMetrics: PropTypes.arrayOf(savedMetricType), formData: PropTypes.shape({ - filters: PropTypes.arrayOf(legacyFilterShape), - having: PropTypes.string, - having_filters: PropTypes.arrayOf(legacyFilterShape), metric: PropTypes.oneOfType([PropTypes.string, adhocMetricType]), metrics: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, adhocMetricType])), - where: PropTypes.string, }), }; @@ -55,13 +45,15 @@ export default class AdhocFilterControl extends React.Component { constructor(props) { super(props); - this.coerceAdhocFilters = this.coerceAdhocFilters.bind(this); this.optionsForSelect = this.optionsForSelect.bind(this); this.onFilterEdit = this.onFilterEdit.bind(this); this.onChange = this.onChange.bind(this); this.getMetricExpression = this.getMetricExpression.bind(this); - const filters = this.coerceAdhocFilters(this.props.value, this.props.formData); + const filters = (this.props.value || []).map(filter => ( + isDictionaryForAdhocFilter(filter) ? new AdhocFilter(filter) : filter + )); + this.optionRenderer = VirtualizedRendererWrap(option => ( )); @@ -87,7 +79,11 @@ export default class AdhocFilterControl extends React.Component { this.setState({ options: this.optionsForSelect(nextProps) }); } if (this.props.value !== nextProps.value) { - this.setState({ values: this.coerceAdhocFilters(nextProps.value, nextProps.formData) }); + this.setState({ + values: (nextProps.value || []).map( + filter => (isDictionaryForAdhocFilter(filter) ? new AdhocFilter(filter) : filter + )), + }); } } @@ -147,62 +143,6 @@ export default class AdhocFilterControl extends React.Component { )).expression; } - coerceAdhocFilters(propsValues, formData) { - // this converts filters from the four legacy filter controls into adhoc filters in the case - // someone loads an old slice in the explore view - if (propsValues) { - return propsValues.map(filter => ( - isDictionaryForAdhocFilter(filter) ? new AdhocFilter(filter) : filter - )); - } - return [ - ...(formData.filters || []).map(filter => ( - new AdhocFilter({ - subject: filter.col, - operator: filter.op, - comparator: filter.val, - clause: CLAUSES.WHERE, - expressionType: EXPRESSION_TYPES.SIMPLE, - filterOptionName: this.generateConvertedFilterOptionName(), - }) - )), - ...(formData.having_filters || []).map(filter => ( - new AdhocFilter({ - subject: filter.col, - operator: filter.op, - comparator: filter.val, - clause: CLAUSES.HAVING, - expressionType: EXPRESSION_TYPES.SIMPLE, - filterOptionName: this.generateConvertedFilterOptionName(), - }) - )), - ...[ - formData.where ? - new AdhocFilter({ - sqlExpression: formData.where, - clause: CLAUSES.WHERE, - expressionType: EXPRESSION_TYPES.SQL, - filterOptionName: this.generateConvertedFilterOptionName(), - }) : - null, - ], - ...[ - formData.having ? - new AdhocFilter({ - sqlExpression: formData.having, - clause: CLAUSES.HAVING, - expressionType: EXPRESSION_TYPES.SQL, - filterOptionName: this.generateConvertedFilterOptionName(), - }) : - null, - ], - ].filter(option => option); - } - - generateConvertedFilterOptionName() { - return `form_filter_${Math.random().toString(36).substring(2, 15)}_${Math.random().toString(36).substring(2, 15)}`; - } - optionsForSelect(props) { const options = [ ...props.columns, diff --git a/superset/assets/src/explore/components/controls/Filter.jsx b/superset/assets/src/explore/components/controls/Filter.jsx deleted file mode 100644 index 539a4133a..000000000 --- a/superset/assets/src/explore/components/controls/Filter.jsx +++ /dev/null @@ -1,187 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import Select from 'react-select'; -import { Button, Row, Col } from 'react-bootstrap'; -import { t } from '../../../locales'; -import SelectControl from './SelectControl'; - -const operatorsArr = [ - { val: 'in', type: 'array', useSelect: true, multi: true }, - { val: 'not in', type: 'array', useSelect: true, multi: true }, - { val: '==', type: 'string', useSelect: true, multi: false, havingOnly: true }, - { val: '!=', type: 'string', useSelect: true, multi: false, havingOnly: true }, - { val: '>=', type: 'string', havingOnly: true }, - { val: '<=', type: 'string', havingOnly: true }, - { val: '>', type: 'string', havingOnly: true }, - { val: '<', type: 'string', havingOnly: true }, - { val: 'regex', type: 'string', datasourceTypes: ['druid'] }, - { val: 'LIKE', type: 'string', datasourceTypes: ['table'] }, - { val: 'IS NULL', type: null }, - { val: 'IS NOT NULL', type: null }, -]; -const operators = {}; -operatorsArr.forEach((op) => { - operators[op.val] = op; -}); - -const propTypes = { - changeFilter: PropTypes.func, - removeFilter: PropTypes.func, - filter: PropTypes.object.isRequired, - datasource: PropTypes.object, - having: PropTypes.bool, - valuesLoading: PropTypes.bool, - valueChoices: PropTypes.array, -}; - -const defaultProps = { - changeFilter: () => {}, - removeFilter: () => {}, - datasource: null, - having: false, - valuesLoading: false, - valueChoices: [], -}; - -export default class Filter extends React.Component { - - switchFilterValue(prevOp, nextOp) { - if (operators[prevOp].type !== operators[nextOp].type) { - // Switch from array to string or vice versa - const val = this.props.filter.val; - let newVal; - if (operators[nextOp].type === 'string') { - if (!val || !val.length) { - newVal = ''; - } else { - newVal = val[0]; - } - } else if (operators[nextOp].type === 'array') { - if (!val || !val.length) { - newVal = []; - } else { - newVal = [val]; - } - } - this.props.changeFilter(['val', 'op'], [newVal, nextOp]); - } else { - // No value type change - this.props.changeFilter('op', nextOp); - } - } - - changeText(event) { - this.props.changeFilter('val', event.target.value); - } - - changeSelect(value) { - this.props.changeFilter('val', value); - } - - changeColumn(event) { - this.props.changeFilter('col', event.value); - } - - changeOp(event) { - this.switchFilterValue(this.props.filter.op, event.value); - } - - removeFilter(filter) { - this.props.removeFilter(filter); - } - - renderFilterFormControl(filter) { - const operator = operators[filter.op]; - if (operator.type === null) { - // IS NULL or IS NOT NULL - return null; - } - if (operator.useSelect && !this.props.having) { - // TODO should use a simple Select, not a control here... - return ( - - ); - } - return ( - - ); - } - render() { - const datasource = this.props.datasource; - const filter = this.props.filter; - const opsChoices = operatorsArr - .filter((o) => { - if (this.props.having) { - return !!o.havingOnly; - } - return (!o.datasourceTypes || o.datasourceTypes.indexOf(datasource.type) >= 0); - }) - .map(o => ({ value: o.val, label: o.val })); - let colChoices; - if (datasource) { - if (this.props.having) { - colChoices = datasource.metrics_combo.map(c => ({ value: c[0], label: c[1] })); - } else { - colChoices = datasource.filterable_cols.map(c => ({ value: c[0], label: c[1] })); - } - } - return ( -
- - - - - - {this.renderFilterFormControl(filter)} - - - - - -
- ); - } -} - -Filter.propTypes = propTypes; -Filter.defaultProps = defaultProps; diff --git a/superset/assets/src/explore/components/controls/FilterControl.jsx b/superset/assets/src/explore/components/controls/FilterControl.jsx deleted file mode 100644 index 041dd6fe2..000000000 --- a/superset/assets/src/explore/components/controls/FilterControl.jsx +++ /dev/null @@ -1,155 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Button, Row, Col } from 'react-bootstrap'; -import Filter from './Filter'; -import { t } from '../../../locales'; - -const $ = window.$ = require('jquery'); - -const propTypes = { - name: PropTypes.string, - onChange: PropTypes.func, - value: PropTypes.array, - datasource: PropTypes.object, -}; - -const defaultProps = { - onChange: () => {}, - value: [], -}; - -export default class FilterControl extends React.Component { - - constructor(props) { - super(props); - const initialFilters = props.value.map(() => ({ - valuesLoading: false, - valueChoices: [], - })); - this.state = { - filters: initialFilters, - activeRequest: null, - }; - } - - componentDidMount() { - this.state.filters.forEach((filter, index) => this.fetchFilterValues(index)); - } - - fetchFilterValues(index, column) { - const datasource = this.props.datasource; - const col = column || this.props.value[index].col; - const having = this.props.name === 'having_filters'; - if (col && this.props.datasource && this.props.datasource.filter_select && !having) { - this.setState((prevState) => { - const newStateFilters = Object.assign([], prevState.filters); - newStateFilters[index].valuesLoading = true; - return { filters: newStateFilters }; - }); - // if there is an outstanding request to fetch values, cancel it. - if (this.state.activeRequest) { - this.state.activeRequest.abort(); - } - this.setState({ - activeRequest: $.ajax({ - type: 'GET', - url: `/superset/filter/${datasource.type}/${datasource.id}/${col}/`, - success: (data) => { - this.setState((prevState) => { - const newStateFilters = Object.assign([], prevState.filters); - newStateFilters[index] = { valuesLoading: false, valueChoices: data }; - return { filters: newStateFilters, activeRequest: null }; - }); - }, - }), - }); - } - } - - addFilter() { - const newFilters = Object.assign([], this.props.value); - const col = this.props.datasource && this.props.datasource.filterable_cols.length > 0 ? - this.props.datasource.filterable_cols[0][0] : - null; - newFilters.push({ - col, - op: 'in', - val: this.props.datasource.filter_select ? [] : '', - }); - this.props.onChange(newFilters); - const nextIndex = this.state.filters.length; - this.setState((prevState) => { - const newStateFilters = Object.assign([], prevState.filters); - newStateFilters.push({ valuesLoading: false, valueChoices: [] }); - return { filters: newStateFilters }; - }); - this.fetchFilterValues(nextIndex, col); - } - - changeFilter(index, control, value) { - const newFilters = Object.assign([], this.props.value); - const modifiedFilter = Object.assign({}, newFilters[index]); - if (typeof control === 'string') { - modifiedFilter[control] = value; - } else { - control.forEach((c, i) => { - modifiedFilter[c] = value[i]; - }); - } - // Clear selected values and refresh upon column change - if (control === 'col') { - if (modifiedFilter.val.constructor === Array) { - modifiedFilter.val = []; - } else if (typeof modifiedFilter.val === 'string') { - modifiedFilter.val = ''; - } - this.fetchFilterValues(index, value); - } - newFilters.splice(index, 1, modifiedFilter); - this.props.onChange(newFilters); - } - - removeFilter(index) { - this.props.onChange(this.props.value.filter((f, i) => i !== index)); - this.setState((prevState) => { - const newStateFilters = Object.assign([], prevState.filters); - newStateFilters.splice(index, 1); - return { filters: newStateFilters }; - }); - } - - render() { - const filters = this.props.value.map((filter, i) => ( -
- -
- )); - return ( -
- {filters} - - - - - -
- ); - } -} - -FilterControl.propTypes = propTypes; -FilterControl.defaultProps = defaultProps; diff --git a/superset/assets/src/explore/components/controls/index.js b/superset/assets/src/explore/components/controls/index.js index 81991275e..4a2df4237 100644 --- a/superset/assets/src/explore/components/controls/index.js +++ b/superset/assets/src/explore/components/controls/index.js @@ -6,7 +6,6 @@ import ColorPickerControl from './ColorPickerControl'; import ColorSchemeControl from './ColorSchemeControl'; import DatasourceControl from './DatasourceControl'; import DateFilterControl from './DateFilterControl'; -import FilterControl from './FilterControl'; import FixedOrMetricControl from './FixedOrMetricControl'; import HiddenControl from './HiddenControl'; import SelectAsyncControl from './SelectAsyncControl'; @@ -29,7 +28,6 @@ const controlMap = { ColorSchemeControl, DatasourceControl, DateFilterControl, - FilterControl, FixedOrMetricControl, HiddenControl, SelectAsyncControl, diff --git a/superset/assets/src/explore/controls.jsx b/superset/assets/src/explore/controls.jsx index b2382c1ca..0281fe10b 100644 --- a/superset/assets/src/explore/controls.jsx +++ b/superset/assets/src/explore/controls.jsx @@ -1132,34 +1132,6 @@ export const controls = { default: '', }, - where: { - type: 'TextAreaControl', - label: t('Custom WHERE clause'), - default: '', - language: 'sql', - minLines: 2, - maxLines: 10, - offerEditInModal: false, - description: t('The text in this box gets included in your query\'s WHERE ' + - 'clause, as an AND to other criteria. You can include ' + - 'complex expression, parenthesis and anything else ' + - 'supported by the backend it is directed towards.'), - }, - - having: { - type: 'TextAreaControl', - label: t('Custom HAVING clause'), - default: '', - language: 'sql', - minLines: 2, - maxLines: 10, - offerEditInModal: false, - description: t('The text in this box gets included in your query\'s HAVING ' + - 'clause, as an AND to other criteria. You can include ' + - 'complex expression, parenthesis and anything else ' + - 'supported by the backend it is directed towards.'), - }, - compare_lag: { type: 'TextControl', label: t('Comparison Period Lag'), @@ -1822,16 +1794,6 @@ export const controls = { description: t('Labels for the marker lines'), }, - filters: { - type: 'FilterControl', - label: '', - default: [], - description: '', - mapStateToProps: state => ({ - datasource: state.datasource, - }), - }, - annotation_layers: { type: 'AnnotationLayerControl', label: '', @@ -1854,18 +1816,6 @@ export const controls = { provideFormDataToProps: true, }, - having_filters: { - type: 'FilterControl', - label: '', - default: [], - description: '', - mapStateToProps: state => ({ - choices: (state.datasource) ? state.datasource.metrics_combo - .concat(state.datasource.filterable_cols) : [], - datasource: state.datasource, - }), - }, - slice_id: { type: 'HiddenControl', label: t('Chart ID'), diff --git a/superset/assets/src/explore/store.js b/superset/assets/src/explore/store.js index 32c132fd4..48a24df51 100644 --- a/superset/assets/src/explore/store.js +++ b/superset/assets/src/explore/store.js @@ -68,14 +68,6 @@ export function getControlsState(state, form_data) { delete formData[k]; } } - // Removing invalid filters that point to a now inexisting column - if (control.type === 'FilterControl' && control.choices) { - if (!formData[k]) { - formData[k] = []; - } - const choiceValues = control.choices.map(c => c[0]); - formData[k] = formData[k].filter(flt => choiceValues.indexOf(flt.col) >= 0); - } if (typeof control.default === 'function') { control.default = control.default(control); diff --git a/superset/assets/src/explore/visTypes.js b/superset/assets/src/explore/visTypes.js index f771c38ad..b0037ec5f 100644 --- a/superset/assets/src/explore/visTypes.js +++ b/superset/assets/src/explore/visTypes.js @@ -40,14 +40,6 @@ export const sections = { ['since', 'until'], ], }, - sqlClause: { - label: t('SQL'), - controlSetRows: [ - ['where'], - ['having'], - ], - description: t('This section exposes ways to include snippets of SQL in your query'), - }, annotations: { label: t('Annotations and Layers'), expanded: true, @@ -80,20 +72,6 @@ export const sections = { ], }, ], - filters: [ - { - label: t('Filters'), - expanded: true, - controlSetRows: [['filters']], - }, - { - label: t('Result Filters'), - expanded: true, - description: t('The filters to apply after post-aggregation.' + - 'Leave the value control empty to filter empty strings or nulls'), - controlSetRows: [['having_filters']], - }, - ], }; const timeGrainSqlaAnimationOverrides = { @@ -263,6 +241,13 @@ export const visTypes = { ['line_charts_2', 'y_axis_2_format'], ], }, + { + label: t('Query'), + expanded: true, + controlSetRows: [ + ['adhoc_filters'], + ], + }, sections.annotations, ], controlOverrides: { @@ -279,8 +264,6 @@ export const visTypes = { }, }, sectionOverrides: { - sqlClause: [], - filters: [[]], datasourceAndVizType: { label: t('Chart Type'), controlSetRows: [ @@ -310,7 +293,9 @@ export const visTypes = { label: t('Query'), expanded: true, controlSetRows: [ - ['metric', 'freq'], + ['metric'], + ['adhoc_filters'], + ['freq'], ], }, { @@ -373,6 +358,13 @@ export const visTypes = { ['metric_2', 'y_axis_2_format'], ], }, + { + label: t('Query'), + expanded: true, + controlSetRows: [ + ['adhoc_filters'], + ], + }, sections.annotations, ], controlOverrides: { @@ -489,6 +481,13 @@ export const visTypes = { ['deck_slices', null], ], }, + { + label: t('Query'), + expanded: true, + controlSetRows: [ + ['adhoc_filters'], + ], + }, ], }, @@ -502,6 +501,7 @@ export const visTypes = { controlSetRows: [ ['spatial', 'size'], ['row_limit', null], + ['adhoc_filters'], ], }, { @@ -540,6 +540,7 @@ export const visTypes = { controlSetRows: [ ['spatial', 'size'], ['row_limit', null], + ['adhoc_filters'], ], }, { @@ -579,6 +580,7 @@ export const visTypes = { controlSetRows: [ ['line_column', 'line_type'], ['row_limit', null], + ['adhoc_filters'], ], }, { @@ -612,6 +614,7 @@ export const visTypes = { controlSetRows: [ ['spatial', 'size'], ['row_limit', null], + ['adhoc_filters'], ], }, { @@ -656,6 +659,7 @@ export const visTypes = { expanded: true, controlSetRows: [ ['geojson', 'row_limit'], + ['adhoc_filters'], ], }, { @@ -696,6 +700,7 @@ export const visTypes = { controlSetRows: [ ['line_column', 'line_type'], ['row_limit', null], + ['adhoc_filters'], ], }, { @@ -736,6 +741,7 @@ export const visTypes = { controlSetRows: [ ['start_spatial', 'end_spatial'], ['row_limit', null], + ['adhoc_filters'], ], }, { @@ -784,6 +790,7 @@ export const visTypes = { expanded: true, controlSetRows: [ ['spatial', 'row_limit'], + ['adhoc_filters'], ], }, { @@ -901,6 +908,13 @@ export const visTypes = { ['row_limit', null], ], }, + { + label: t('Query'), + expanded: true, + controlSetRows: [ + ['adhoc_filters'], + ], + }, { label: t('Options'), expanded: true, @@ -929,7 +943,9 @@ export const visTypes = { label: t('Query'), expanded: true, controlSetRows: [ - ['groupby', 'metrics'], + ['metrics'], + ['adhoc_filters'], + ['groupby'], ['limit'], ['column_collection'], ['url'], @@ -969,8 +985,10 @@ export const visTypes = { label: t('Query'), expanded: true, controlSetRows: [ - ['groupby', 'columns'], ['metrics'], + ['adhoc_filters'], + ['groupby'], + ['columns'], ['row_limit', null], ], }, @@ -1017,7 +1035,9 @@ export const visTypes = { label: t('Query'), expanded: true, controlSetRows: [ - ['series', 'metric'], + ['metric'], + ['adhoc_filters'], + ['series'], ['row_limit', null], ], }, @@ -1040,6 +1060,7 @@ export const visTypes = { expanded: true, controlSetRows: [ ['metrics'], + ['adhoc_filters'], ['groupby'], ], }, @@ -1070,6 +1091,7 @@ export const visTypes = { controlSetRows: [ ['domain_granularity', 'subdomain_granularity'], ['metrics'], + ['adhoc_filters'], ], }, { @@ -1106,7 +1128,9 @@ export const visTypes = { expanded: true, controlSetRows: [ ['metrics'], - ['groupby', 'limit'], + ['adhoc_filters'], + ['groupby'], + ['limit'], ], }, { @@ -1128,8 +1152,11 @@ export const visTypes = { expanded: true, controlSetRows: [ ['series', 'entity'], - ['x', 'y'], - ['size', 'max_bubble_size'], + ['x'], + ['y'], + ['adhoc_filters'], + ['size'], + ['max_bubble_size'], ['limit', null], ], }, @@ -1179,6 +1206,7 @@ export const visTypes = { expanded: true, controlSetRows: [ ['metric'], + ['adhoc_filters'], ], }, { @@ -1256,6 +1284,7 @@ export const visTypes = { expanded: true, controlSetRows: [ ['all_columns_x'], + ['adhoc_filters'], ['row_limit'], ['groupby'], ], @@ -1298,7 +1327,9 @@ export const visTypes = { expanded: true, controlSetRows: [ ['groupby'], - ['metric', 'secondary_metric'], + ['metric'], + ['secondary_metric'], + ['adhoc_filters'], ['row_limit'], ], }, @@ -1338,6 +1369,7 @@ export const visTypes = { controlSetRows: [ ['groupby'], ['metric'], + ['adhoc_filters'], ['row_limit'], ], }, @@ -1366,6 +1398,7 @@ export const visTypes = { controlSetRows: [ ['groupby'], ['metric'], + ['adhoc_filters'], ['row_limit'], ], }, @@ -1391,8 +1424,11 @@ export const visTypes = { label: t('Query'), expanded: true, controlSetRows: [ - ['groupby', 'columns'], - ['metric', 'row_limit'], + ['groupby'], + ['columns'], + ['metric'], + ['adhoc_filters'], + ['row_limit'], ], }, { @@ -1432,6 +1468,7 @@ export const visTypes = { controlSetRows: [ ['entity'], ['metric'], + ['adhoc_filters'], ], }, { @@ -1467,6 +1504,7 @@ export const visTypes = { ['entity'], ['country_fieldtype'], ['metric'], + ['adhoc_filters'], ], }, { @@ -1503,6 +1541,7 @@ export const visTypes = { controlSetRows: [ ['groupby'], ['metric'], + ['adhoc_filters'], ['date_filter', 'instant_filtering'], ['show_sqla_time_granularity', 'show_sqla_time_column'], ['show_druid_time_granularity', 'show_druid_time_origin'], @@ -1544,6 +1583,7 @@ export const visTypes = { ['series'], ['metrics'], ['secondary_metric'], + ['adhoc_filters'], ['limit'], ], }, @@ -1564,7 +1604,9 @@ export const visTypes = { expanded: true, controlSetRows: [ ['all_columns_x', 'all_columns_y'], - ['metric', 'row_limit'], + ['metric'], + ['adhoc_filters'], + ['row_limit'], ], }, { @@ -1627,6 +1669,7 @@ export const visTypes = { ['all_columns_x', 'all_columns_y'], ['clustering_radius'], ['row_limit'], + ['adhoc_filters'], ['groupby'], ], }, @@ -1701,6 +1744,13 @@ export const visTypes = { ['min_leaf_node_event_count'], ], }, + { + label: t('Query'), + expanded: true, + controlSetRows: [ + ['adhoc_filters'], + ], + }, { label: t('Additional meta data'), controlSetRows: [ @@ -1799,12 +1849,6 @@ export const visTypes = { export default visTypes; -function adhocFilterEnabled(viz) { - return viz.controlPanelSections.find(( - section => section.controlSetRows.find(row => row.find(control => control === 'adhoc_filters')) - )); -} - export function sectionsToRender(vizType, datasourceType) { const viz = visTypes[vizType]; @@ -1826,7 +1870,5 @@ export function sectionsToRender(vizType, datasourceType) { sectionsCopy.datasourceAndVizType, datasourceType === 'table' ? sectionsCopy.sqlaTimeSeries : sectionsCopy.druidTimeSeries, viz.controlPanelSections, - !adhocFilterEnabled(viz) && (datasourceType === 'table' ? sectionsCopy.sqlClause : []), - !adhocFilterEnabled(viz) && (datasourceType === 'table' ? sectionsCopy.filters[0] : sectionsCopy.filters), ).filter(section => section); } diff --git a/superset/migrations/versions/bddc498dd179_adhoc_filters.py b/superset/migrations/versions/bddc498dd179_adhoc_filters.py new file mode 100644 index 000000000..55503f007 --- /dev/null +++ b/superset/migrations/versions/bddc498dd179_adhoc_filters.py @@ -0,0 +1,97 @@ +"""adhoc filters + +Revision ID: bddc498dd179 +Revises: afb7730f6a9c +Create Date: 2018-06-13 14:54:47.086507 + +""" + +# revision identifiers, used by Alembic. +revision = 'bddc498dd179' +down_revision = '80a67c5192fa' + + +from collections import defaultdict +import json +import uuid + +from alembic import op +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import Column, Integer, Text + +from superset import db +from superset import utils + + +Base = declarative_base() + + +class Slice(Base): + __tablename__ = 'slices' + + id = Column(Integer, primary_key=True) + params = Column(Text) + + +def upgrade(): + bind = op.get_bind() + session = db.Session(bind=bind) + mapping = {'having': 'having_filters', 'where': 'filters'} + + for slc in session.query(Slice).all(): + try: + params = json.loads(slc.params) + + if not 'adhoc_filters' in params: + params['adhoc_filters'] = [] + + for clause, filters in mapping.items(): + if clause in params and params[clause] != '': + params['adhoc_filters'].append({ + 'clause': clause.upper(), + 'expressionType': 'SQL', + 'filterOptionName': str(uuid.uuid4()), + 'sqlExpression': params[clause], + }) + + if filters in params: + for filt in params[filters]: + params['adhoc_filters'].append({ + 'clause': clause.upper(), + 'comparator': filt['val'], + 'expressionType': 'SIMPLE', + 'filterOptionName': str(uuid.uuid4()), + 'operator': filt['op'], + 'subject': filt['col'], + }) + + for key in ('filters', 'having', 'having_filters', 'where'): + if key in params: + del params[key] + + slc.params = json.dumps(params, sort_keys=True) + except Exception: + pass + + session.commit() + session.close() + + +def downgrade(): + bind = op.get_bind() + session = db.Session(bind=bind) + + for slc in session.query(Slice).all(): + try: + params = json.loads(slc.params) + utils.split_adhoc_filters_into_base_filters(params) + + if 'adhoc_filters' in params: + del params['adhoc_filters'] + + slc.params = json.dumps(params, sort_keys=True) + except Exception: + pass + + session.commit() + session.close() diff --git a/superset/translations/de/LC_MESSAGES/messages.po b/superset/translations/de/LC_MESSAGES/messages.po index 7db79cda0..59077e21a 100644 --- a/superset/translations/de/LC_MESSAGES/messages.po +++ b/superset/translations/de/LC_MESSAGES/messages.po @@ -1193,7 +1193,6 @@ msgstr "" msgid "Select operator" msgstr "" -#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:138 #: superset/templates/appbuilder/general/widgets/search.html:6 msgid "Add Filter" msgstr "" diff --git a/superset/translations/en/LC_MESSAGES/messages.po b/superset/translations/en/LC_MESSAGES/messages.po index 3d5e245f4..a861350db 100644 --- a/superset/translations/en/LC_MESSAGES/messages.po +++ b/superset/translations/en/LC_MESSAGES/messages.po @@ -3815,7 +3815,6 @@ msgstr "" msgid "Select operator" msgstr "" -#: superset/assets/src/explore/components/controls/FilterControl.jsx:145 #: superset/templates/appbuilder/general/widgets/search.html:6 msgid "Add Filter" msgstr "" diff --git a/superset/translations/es/LC_MESSAGES/messages.po b/superset/translations/es/LC_MESSAGES/messages.po index 4742f7637..1dbd4649c 100644 --- a/superset/translations/es/LC_MESSAGES/messages.po +++ b/superset/translations/es/LC_MESSAGES/messages.po @@ -1250,7 +1250,6 @@ msgstr "Selecciona la columna" msgid "Select operator" msgstr "Selecciona el operador" -#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:138 #: superset/templates/appbuilder/general/widgets/search.html:6 msgid "Add Filter" msgstr "Añadir Filtro" diff --git a/superset/translations/fr/LC_MESSAGES/messages.po b/superset/translations/fr/LC_MESSAGES/messages.po index f3914a408..16245c33b 100644 --- a/superset/translations/fr/LC_MESSAGES/messages.po +++ b/superset/translations/fr/LC_MESSAGES/messages.po @@ -3819,7 +3819,6 @@ msgstr "" msgid "Select operator" msgstr "" -#: superset/assets/src/explore/components/controls/FilterControl.jsx:145 #: superset/templates/appbuilder/general/widgets/search.html:6 msgid "Add Filter" msgstr "Ajouter un filtre" diff --git a/superset/translations/it/LC_MESSAGES/messages.po b/superset/translations/it/LC_MESSAGES/messages.po index 5ef5b4b28..f383e14d5 100644 --- a/superset/translations/it/LC_MESSAGES/messages.po +++ b/superset/translations/it/LC_MESSAGES/messages.po @@ -1598,7 +1598,6 @@ msgstr "Seleziona una colonna" msgid "Select operator" msgstr "Seleziona operatore" -#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:145 #: superset/templates/appbuilder/general/widgets/search.html:6 msgid "Add Filter" msgstr "Aggiungi filtro" diff --git a/superset/translations/ja/LC_MESSAGES/messages.po b/superset/translations/ja/LC_MESSAGES/messages.po index 5f6b714f5..df1618511 100644 --- a/superset/translations/ja/LC_MESSAGES/messages.po +++ b/superset/translations/ja/LC_MESSAGES/messages.po @@ -1202,7 +1202,6 @@ msgstr "列を選択" msgid "Select operator" msgstr "オペレータを選択" -#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:138 #: superset/templates/appbuilder/general/widgets/search.html:6 msgid "Add Filter" msgstr "フィルターを追加" diff --git a/superset/translations/messages.pot b/superset/translations/messages.pot index 23d2cb4a3..09c3e3692 100644 --- a/superset/translations/messages.pot +++ b/superset/translations/messages.pot @@ -3814,7 +3814,6 @@ msgstr "" msgid "Select operator" msgstr "" -#: superset/assets/src/explore/components/controls/FilterControl.jsx:145 #: superset/templates/appbuilder/general/widgets/search.html:6 msgid "Add Filter" msgstr "" diff --git a/superset/translations/pt_BR/LC_MESSAGES/messages.po b/superset/translations/pt_BR/LC_MESSAGES/messages.po index b2a5b3474..871002123 100644 --- a/superset/translations/pt_BR/LC_MESSAGES/messages.po +++ b/superset/translations/pt_BR/LC_MESSAGES/messages.po @@ -1256,7 +1256,6 @@ msgstr "Selecione a coluna" msgid "Select operator" msgstr "Selecione o operador" -#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:138 #: superset/templates/appbuilder/general/widgets/search.html:6 msgid "Add Filter" msgstr "Adicionar filtro" diff --git a/superset/translations/ru/LC_MESSAGES/messages.po b/superset/translations/ru/LC_MESSAGES/messages.po index 79e5713a4..5ea2cc1b5 100644 --- a/superset/translations/ru/LC_MESSAGES/messages.po +++ b/superset/translations/ru/LC_MESSAGES/messages.po @@ -1599,7 +1599,6 @@ msgstr "Выбрать столбец" msgid "Select operator" msgstr "Выбрать оператор" -#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:145 #: superset/templates/appbuilder/general/widgets/search.html:6 msgid "Add Filter" msgstr "Добавить фильтр" diff --git a/superset/translations/zh/LC_MESSAGES/messages.po b/superset/translations/zh/LC_MESSAGES/messages.po index 0a9cc2fce..4cd37c9a9 100644 --- a/superset/translations/zh/LC_MESSAGES/messages.po +++ b/superset/translations/zh/LC_MESSAGES/messages.po @@ -3820,7 +3820,6 @@ msgstr "选择列" msgid "Select operator" msgstr "选择运算符" -#: superset/assets/src/explore/components/controls/FilterControl.jsx:145 #: superset/templates/appbuilder/general/widgets/search.html:6 msgid "Add Filter" msgstr "增加过滤条件" diff --git a/superset/utils.py b/superset/utils.py index 08ce0d2f3..eec253037 100644 --- a/superset/utils.py +++ b/superset/utils.py @@ -881,4 +881,3 @@ def split_adhoc_filters_into_base_filters(fd): fd['having'] = ' AND '.join(['({})'.format(sql) for sql in sql_having_filters]) fd['having_filters'] = simple_having_filters fd['filters'] = simple_where_filters - del fd['adhoc_filters']