diff --git a/licenses.yaml b/licenses.yaml index 4e5d201edb77..2c24535782f8 100644 --- a/licenses.yaml +++ b/licenses.yaml @@ -5784,6 +5784,16 @@ license_file_path: licenses/bin/date-fns.MIT --- +name: "dayjs" +license_category: binary +module: web-console +license_name: MIT License +copyright: iamkun +version: 1.11.15 +license_file_path: licenses/bin/dayjs.MIT + +--- + name: "delayed-stream" license_category: binary module: web-console @@ -5838,7 +5848,7 @@ license_category: binary module: web-console license_name: Apache License version 2.0 copyright: Imply Data -version: 1.2.0 +version: 1.2.1 --- diff --git a/licenses/bin/dayjs.MIT b/licenses/bin/dayjs.MIT new file mode 100644 index 000000000000..caf9315493c6 --- /dev/null +++ b/licenses/bin/dayjs.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-present, iamkun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/web-console/package-lock.json b/web-console/package-lock.json index 16f1050c6775..07c2b1947408 100644 --- a/web-console/package-lock.json +++ b/web-console/package-lock.json @@ -31,7 +31,7 @@ "d3-time-format": "^4.1.0", "date-fns": "^2.28.0", "dayjs": "^1.11.15", - "druid-query-toolkit": "^1.2.0", + "druid-query-toolkit": "^1.2.1", "echarts": "^5.5.1", "file-saver": "^2.0.5", "hjson": "^3.2.2", @@ -7290,9 +7290,9 @@ } }, "node_modules/druid-query-toolkit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-1.2.0.tgz", - "integrity": "sha512-MMcr1012UZSbOG+O3bI1BxxGQOHM1sVwPWYGgbQlfnBCQovjcLDAxkzWUGY/nn1c3B+Yrblcp3oMRl/S0HpiDQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-1.2.1.tgz", + "integrity": "sha512-vg+r0J0cbK934QhqNaHs/Zs0nXYZSv//ZnLor71OCsP73ya60SBaN3nW3DuR+TxUGMIgZpSIgGQbG03eP7vfwA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.5.2" @@ -8871,6 +8871,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -23710,9 +23711,9 @@ } }, "druid-query-toolkit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-1.2.0.tgz", - "integrity": "sha512-MMcr1012UZSbOG+O3bI1BxxGQOHM1sVwPWYGgbQlfnBCQovjcLDAxkzWUGY/nn1c3B+Yrblcp3oMRl/S0HpiDQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-1.2.1.tgz", + "integrity": "sha512-vg+r0J0cbK934QhqNaHs/Zs0nXYZSv//ZnLor71OCsP73ya60SBaN3nW3DuR+TxUGMIgZpSIgGQbG03eP7vfwA==", "requires": { "tslib": "^2.5.2" } diff --git a/web-console/package.json b/web-console/package.json index b97764e18408..d8261ee0829d 100644 --- a/web-console/package.json +++ b/web-console/package.json @@ -73,7 +73,7 @@ "d3-time-format": "^4.1.0", "date-fns": "^2.28.0", "dayjs": "^1.11.15", - "druid-query-toolkit": "^1.2.0", + "druid-query-toolkit": "^1.2.1", "echarts": "^5.5.1", "file-saver": "^2.0.5", "hjson": "^3.2.2", diff --git a/web-console/src/views/workbench-view/query-parameters-dialog/__snapshots__/query-parameters-dialog.spec.tsx.snap b/web-console/src/views/workbench-view/query-parameters-dialog/__snapshots__/query-parameters-dialog.spec.tsx.snap index 576751c66f1b..84a8ef44991f 100644 --- a/web-console/src/views/workbench-view/query-parameters-dialog/__snapshots__/query-parameters-dialog.spec.tsx.snap +++ b/web-console/src/views/workbench-view/query-parameters-dialog/__snapshots__/query-parameters-dialog.spec.tsx.snap @@ -1,5 +1,373 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`QueryParametersDialog handles ARRAY type parameters correctly 1`] = ` + +
+

+ Druid SQL supports dynamic parameters using question mark + + ? + + syntax, where parameters are bound positionally to ? placeholders at execution time. +

+ + + + + + + + + + + } + defaultIsOpen={false} + disabled={false} + fill={false} + hasBackdrop={false} + hoverCloseDelay={300} + hoverOpenDelay={150} + inheritDarkTheme={true} + interactionKind="click" + matchTargetWidth={false} + minimal={true} + openOnTargetFocus={true} + position="bottom-left" + positioningStrategy="absolute" + shouldReturnFocusOnClose={false} + targetTagName="span" + transitionDuration={300} + usePortal={true} + > + + + + + + + + + + + + + + + + + } + defaultIsOpen={false} + disabled={false} + fill={false} + hasBackdrop={false} + hoverCloseDelay={300} + hoverOpenDelay={150} + inheritDarkTheme={true} + interactionKind="click" + matchTargetWidth={false} + minimal={true} + openOnTargetFocus={true} + position="bottom-left" + positioningStrategy="absolute" + shouldReturnFocusOnClose={false} + targetTagName="span" + transitionDuration={300} + usePortal={true} + > + + + + + + + + + + + + + + + + + } + defaultIsOpen={false} + disabled={false} + fill={false} + hasBackdrop={false} + hoverCloseDelay={300} + hoverOpenDelay={150} + inheritDarkTheme={true} + interactionKind="click" + matchTargetWidth={false} + minimal={true} + openOnTargetFocus={true} + position="bottom-left" + positioningStrategy="absolute" + shouldReturnFocusOnClose={false} + targetTagName="span" + transitionDuration={300} + usePortal={true} + > + + + + + + + +
+
+
+ + +
+
+
+`; + exports[`QueryParametersDialog matches snapshot 1`] = ` + } defaultIsOpen={false} @@ -177,6 +555,16 @@ exports[`QueryParametersDialog matches snapshot 1`] = ` shouldDismissPopover={true} text="FLOAT" /> + } defaultIsOpen={false} @@ -275,6 +663,16 @@ exports[`QueryParametersDialog matches snapshot 1`] = ` shouldDismissPopover={true} text="FLOAT" /> + } defaultIsOpen={false} @@ -373,6 +771,16 @@ exports[`QueryParametersDialog matches snapshot 1`] = ` shouldDismissPopover={true} text="FLOAT" /> + } defaultIsOpen={false} @@ -410,6 +818,114 @@ exports[`QueryParametersDialog matches snapshot 1`] = ` /> + + + + + + + + + + + } + defaultIsOpen={false} + disabled={false} + fill={false} + hasBackdrop={false} + hoverCloseDelay={300} + hoverOpenDelay={150} + inheritDarkTheme={true} + interactionKind="click" + matchTargetWidth={false} + minimal={true} + openOnTargetFocus={true} + position="bottom-left" + positioningStrategy="absolute" + shouldReturnFocusOnClose={false} + targetTagName="span" + transitionDuration={300} + usePortal={true} + > + + + + + + { { type: 'TIMESTAMP', value: '2022-02-02 01:02:03' }, { type: 'BIGINT', value: 42 }, { type: 'DOUBLE', value: 1.618 }, + { type: 'ARRAY', value: [-25.7, null, 36.85] }, + ]} + onQueryParametersChange={() => {}} + onClose={() => {}} + />, + ); + + expect(comp).toMatchSnapshot(); + }); + + it('handles ARRAY type parameters correctly', () => { + const comp = shallow( + {}} onClose={() => {}} diff --git a/web-console/src/views/workbench-view/query-parameters-dialog/query-parameters-dialog.tsx b/web-console/src/views/workbench-view/query-parameters-dialog/query-parameters-dialog.tsx index 49a9ebb587df..fc61cbe8f099 100644 --- a/web-console/src/views/workbench-view/query-parameters-dialog/query-parameters-dialog.tsx +++ b/web-console/src/views/workbench-view/query-parameters-dialog/query-parameters-dialog.tsx @@ -40,7 +40,26 @@ import { deepSet, oneOf, tickIcon, without } from '../../../utils'; import './query-parameters-dialog.scss'; -const TYPES = ['VARCHAR', 'TIMESTAMP', 'BIGINT', 'DOUBLE', 'FLOAT']; +const TYPES = ['VARCHAR', 'TIMESTAMP', 'BIGINT', 'DOUBLE', 'FLOAT', 'ARRAY']; + +function parseArrayValue(input: string): any[] | string { + try { + const parsed = JSON.parse(input); + if (Array.isArray(parsed)) { + return parsed; + } + return input; + } catch { + return input; + } +} + +function formatArrayValue(value: any): string { + if (Array.isArray(value)) { + return JSON.stringify(value); + } + return value; +} interface QueryParametersDialogProps { queryParameters: QueryParameter[] | undefined; @@ -77,7 +96,11 @@ export const QueryParametersDialog = React.memo(function QueryParametersDialog( const { type, value } = queryParameter; function onValueChange(v: string | number) { - setCurrentQueryParameters(deepSet(currentQueryParameters, `${i}.value`, v)); + let finalValue: any = v; + if (type === 'ARRAY' && typeof v === 'string') { + finalValue = parseArrayValue(v); + } + setCurrentQueryParameters(deepSet(currentQueryParameters, `${i}.value`, finalValue)); } return ( @@ -112,6 +135,13 @@ export const QueryParametersDialog = React.memo(function QueryParametersDialog( fill arbitraryPrecision={type !== 'BIGINT'} /> + ) : type === 'ARRAY' ? ( + onValueChange(e.target.value)} + placeholder="[-25.7, null, 36.85]" + fill + /> ) : (