From 14c81c8f36da5e5392a84def6cce3fff51b40a1c Mon Sep 17 00:00:00 2001 From: cccs-RyanS <71385290+cccs-RyanS@users.noreply.github.com> Date: Wed, 5 Jul 2023 14:17:35 -0400 Subject: [PATCH] Feature/dataset explorer chart (#348) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [cccs-2.0] updating docker file (#188) * [CLDN-1565] Fixing bugs (#189) * [CLDN-1565] Fixing bugs * removing uneeded import * Feature/cldn 1565 (#192) * [CLDN-1565] Fixing bugs * removing uneeded import * [CLDN-1565] Adding new image * Feature/cldn 1609 (#195) * Add row number option to CCCS-Grid (#181) * Add row number option to CCCS-Grid * Have row numbers show by default Co-authored-by: cccs-Dustin <96579982+cccs-Dustin@users.noreply.github.com> Co-authored-by: cccs-Dustin <96579982+cccs-Dustin@users.noreply.github.com> * Use new build agent (#187) * Use new build agent * Remove quotes * Display datahub link for datasets with defined URN (#177) * Display datahub link for datasets with defined URN * Add datahub SVG * Reorder imports * Add datahub link to dashboards * Convert to camel case * Add tests for DatasetList * Add tests for dashboard header * Provide explanation for empty catch * [CLDN-1540] Added ability for multiple user emails to be filtered on at once * [CLDN-1540] temp update to docker image tag so that we can deploy code changes to PB-stg * [CLDN-1540] revert of temp update to docker image tag so that we can deploy code changes to PB-stg * Add path to url (#193) * [CLDN-1609] Fixing post merge build errors * Update cccs-build/superset-base/azure-pipelines.yml Co-authored-by: Reese <10563996+reesercollins@users.noreply.github.com> * Update superset-frontend/src/cccs-viz/plugins/plugin-chart-at-a-glance-user-id/src/styles.js Co-authored-by: Reese <10563996+reesercollins@users.noreply.github.com> Co-authored-by: Reese <10563996+reesercollins@users.noreply.github.com> Co-authored-by: cccs-Dustin <96579982+cccs-Dustin@users.noreply.github.com> * [CLDN-1609] Updating image (#196) * [CLDN-1620] Making rendering use advanced type (#197) * updating image (#198) * [CLDN-1609] Fixing all QA bugs (#201) * [CLDN-1609] Fixing all QA bugs * [CLDN-1609] Fixing build errors * [CLDN-1609] adding additional fixes * [CLDN-1609] Removing log files * [CLDN-1609] Fixing build error Co-authored-by: reesercollins <10563996+reesercollins@users.noreply.github.com> * Feature/cldn 1541 (#200) * [CLDN-1541] Added the ability to filter on multiple user ID's and IP's at the same time in the Application Links custom viz * Temp update to image for deploying to PB-stg * Reverting change to image * [CLDN-1541] Added a tooltip to the Alfred icon * Temp update to base image * Reverting temporary change to image tag * Update superset-frontend/src/cccs-viz/plugins/plugin-chart-application-links/src/ApplicationLinks.tsx Co-authored-by: cccs-rc <62034438+cccs-rc@users.noreply.github.com> * Update superset-frontend/src/cccs-viz/plugins/plugin-chart-application-links/src/ApplicationLinks.tsx Co-authored-by: cccs-rc <62034438+cccs-rc@users.noreply.github.com> * [CLDN-1541] Changed the logic around creating the proper links/URL * Temp update to img * Undo temp img change Co-authored-by: cccs-rc <62034438+cccs-rc@users.noreply.github.com> * [CLDN-1609] Updating docker image (#202) * [cccs-2.0] Fixing case sensitivity (#203) * [cccs-2.0] Fixing case sensitivity * [cccs-2.0] Pusing temp image * [cccs-2.0] fixing two order bys * [cccs-2.0] fixing verbose names, fixing sizing, fixing ordering * [cccs-2.0] fixing build error * [cccs-2.0] updating image * [cccs-2.0] fixing time columns * [cccs-2.0] Fixing build errors * [cccs-2.0] updating docker file * [cccs-2.0] Fixing time column to populate default time col * Fixing default time col * [cccs-2.0] updating image * Fix issue where datasets without a date/datetime column would not load in the explore view * Time column dropdown list is now properly populated with either the default temporal column, or the first temporal column (if a default is not set) * Temp update to base image tag * Fixed the bug where when there are no dttm columns, the non-dttm columns were being displayed * Temp update to base image tag * Reverting changes to the img Co-authored-by: cccs-Dustin <96579982+cccs-Dustin@users.noreply.github.com> * Fix/cccs 2.0 (#204) * [cccs-2.0] Fixing case sensitivity * [cccs-2.0] Pusing temp image * [cccs-2.0] fixing two order bys * [cccs-2.0] fixing verbose names, fixing sizing, fixing ordering * [cccs-2.0] fixing build error * [cccs-2.0] updating image * [cccs-2.0] fixing time columns * [cccs-2.0] Fixing build errors * [cccs-2.0] updating docker file * [cccs-2.0] Fixing time column to populate default time col * Fixing default time col * [cccs-2.0] updating image * Fix issue where datasets without a date/datetime column would not load in the explore view * Time column dropdown list is now properly populated with either the default temporal column, or the first temporal column (if a default is not set) * Temp update to base image tag * Fixed the bug where when there are no dttm columns, the non-dttm columns were being displayed * Temp update to base image tag * Reverting changes to the img * Updating image tag Co-authored-by: cccs-RyanS <71385290+cccs-RyanS@users.noreply.github.com> * Fixed issue with Typescript (#205) * Fix cccs 2.0 (#206) * Fixed issue with Typescript * Update image tag * Feature/cldn 1563 (#207) * Ran pre-commit hook on the front-end * Removed unused file & removed duplicate method * Made changes so that the pre-commit hook would pass all tests * Temp update to build img * revert temp change to build img * Updated image (#208) * [CLDN-1683] Added code to show column descriptions as tooltips in theā€¦ (#209) * [CLDN-1683] Added code to show column descriptions as tooltips in the Hogwarts Table custom visualisation * [CLDN-1683] Temp update to build image * [CLDN-1683] Revert update to image tag * Feature/cldn 1676 (#210) * [CLDN-1676] Added ability to search for the IPv4 rendered value of an IP address instead of the raw value * [CLDN-1676] Temp update to img for deploying to UDev * [CLDN-1676] Undo temp update to img for deploying to UDev * Updating Superset Base Image Tag (#211) * added adhoc filter plugin files * Made sure that adhoc filter uses the adhoc filter object * added adhocfiltercontrol to native adhoc filter * fixed hook that made too many requests * fixed applied filter label * removed duplicate files * Removed uneeded functions * Removed uneeded functions and variables * Removed unused props variables * modifying base image tag * Removed unused config settings * removed column for filter config form * Improved the applied filter(s) modal * Temp update to build image * fixed string formatting issue: * updating superset base image tag * added setFocused hooks to filter when hovering * Fixed the right click to emit dashboard cross-filters (#213) * Fixed the right click to emit filters through the context menu as it was not working * Temp update to build image * Revert temp update to build image * Updated superset base image (#214) * fixed unused declaration error * updating image * Prevent invalid filter values from being saved (#215) By assuming that all input is invalid until the api tells us otherwise, we can prevent the user from clicking the save button in the time between the value being entered and the api returning. * [cccs-2.0] fixing trino req (#216) * updating superset-base image tag * added option to emit a filter from a default column for a row in ag grid * Update trino python client (#219) * Updated trino python client from 0.316.0 to 0.318.0 * Temp update to base image tag * Removing temp change to build image * Updated superset's base image tag (#221) * Update superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/CccsGrid.tsx Co-authored-by: cccs-Dustin <96579982+cccs-Dustin@users.noreply.github.com> * changed control panel to plural * disallow columns that are not a part of the chart to be selected as default emit filters * updated image tag * updated control panel logic for agg mode * update image tag * fixed issues * updating base image * fixed refactor error * updating base image * fixed so that option disables properly * updating base image * Add users API to get and delete users (#223) * Add users API to get and delete users * Allow browser login for users api * updating image * Update cccs-build/superset/Dockerfile Co-authored-by: cccs-Dustin <96579982+cccs-Dustin@users.noreply.github.com> * Added ability to certify entities with multiple values (#224) * Added ability to certify entities with multiple values * Update description text to reflect new feature * Add tests for multiple certified by values * Feature/cldn 1749 (#222) * [CLDN-1749] adding new viz * chanigng to work with standard filters * Adding generic filter extraction * Removing unused imports * [CLDN-1749] Cleaning up code for reusablility * [CLDN-1749] removing unused files * [CLDN-1746] fixing case * [CLDN-1749] adding parameter validation * [CLDN-1749] adding error message and prefix parameter * [CLDN-1749] removing unused files * [CLDN-1749] adding temp base image for docker deployment * [CLDN-1749] Better error handling and new icon * [CLDN-1749] remove unused imports * temp dockerfile change * [CLDN-1749] Adding the adhoc filters back in * [CLDN-1749] Updating image * [CLDN-1749] Adding new thumbnail * Updating dockerfile * [CLDN-1749] Removing uneeded control panel elements * [CLDN-1749] Fixing build errors * [CLDN-1749] New image * Change to use data from dataset * Removing unsed function * Fixing typos * renove unused import * updating image * error message and label fixes * updating image * Update cccs-build/superset/Dockerfile Co-authored-by: cccs-Dustin <96579982+cccs-Dustin@users.noreply.github.com> * Update superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/transformProps.ts Co-authored-by: cccs-RyanK <102618419+cccs-RyanK@users.noreply.github.com> * Update superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/transformProps.ts Co-authored-by: cccs-RyanK <102618419+cccs-RyanK@users.noreply.github.com> Co-authored-by: cccs-Dustin <96579982+cccs-Dustin@users.noreply.github.com> Co-authored-by: cccs-RyanK <102618419+cccs-RyanK@users.noreply.github.com> * Prevent non-admins from using users api (#236) * Prevent non-admins from using users api * Add tests * added styles to highlight adhoc filter when focused and fix awkward empty header * removed suggestions from popping up * added comment * fixed unused import error * updating base image * Update Dockerfile * [CLDN-1829] fixing principle filter logic with state (#234) (#244) * [CLDN-1829] fixing principle filter logic with state * [CLDN-1829] Fixing build error * [cccs-2.0] Updating image (#245) * value set to empty list instead of undefined * updating base image * Feature/cldn 1748 (#243) * [CLDN-1748] adding base component * [CLDN-1748] changing state to use advanced data type * [CLDN-1748] add multi select for musiness type * [CLDN-1748] adding config manager * [CLDN-1748] fixing build errors * temp update of base image * [CLDN-1748] Fixing Url parameter encoding and show name of dashboard drilling to * temp update of image * Pulling extra data from dashboard endpoint * Adding more data to filters object * [CLDN-1748] passing mroe fitler information * [CLDN-1748] QA feedback * [CLDN-1748] Fixing build issues * [CLDN-1748] Updating dockerfile * [CLDN-1748] Updating dockerfile * fixing props name * update image * updating docker file * [CLDN-1748] Name changes * [CLDN-1748] updating image * [CLDN-1748] Adding icon * [CLDN-1748] fixing build errors * [CLDN-1748] update image * [CLDN-1748] fixing context menue to renderering * UPdating docker file * CLDN-1710 including schema in search columns for datasets api (#253) * Update Dockerfile * CLDN-1710 tag update (#255) * update image tag * updating image tag * Update vault image to get new CAs * update image tag * Update Dockerfile * Update Dockerfile * Feature/cldn 1773 (#269) * [CLDN-1773] Adding default group by * Updating DockerFile * Fixing Groupby * Updating base image * [CLDN-1773] Saving Group by order * Updating dockerfile * Update controlPanel.tsx * Update superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/plugin/transformProps.ts Co-authored-by: cccs-tom <59839056+cccs-tom@users.noreply.github.com> * Update superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/plugin/transformProps.ts Co-authored-by: cccs-tom <59839056+cccs-tom@users.noreply.github.com> * Update superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/plugin/transformProps.ts Co-authored-by: cccs-RyanK <102618419+cccs-RyanK@users.noreply.github.com> --------- Co-authored-by: cccs-tom <59839056+cccs-tom@users.noreply.github.com> Co-authored-by: cccs-RyanK <102618419+cccs-RyanK@users.noreply.github.com> * Updating docker file (#270) * Feature/cldn 1968 (Display JSON data inline) (#268) * Add buttons to expand and minimize JSON data as well as ability to expand and/or collapse all rows in a certain column * [CLDN-1968] Added expand button for full row * [CLDN-1968] Resize JSON columns * [CLDN-1968] Added new array which tracks JSON cell state * Revert "[CLDN-1968] Added new array which tracks JSON cell state" This reverts commit dabc3daee96cfbe39d9615837076e54736e518b6. * [CLDN-1968] Added ability for row level expand all button to track if cells are expanded or not * [CLDN-1968] Ran pre-commit hook * [CLDN-1968] Improved UI * [CLDN-1968] Update image tag for testing * [CLDN-1968] Revert image tag for testing * [CLDN-1968] Added multiple UI/UX changes based on QA feedback * [CLDN-1968] Added more UI/UX changes based on QA feedback * [CLDN-1968] Temp change to image * Revert "[CLDN-1968] Temp change to image" This reverts commit 57490bd71dbf14499f08e21e691933d6bbaf914d. * Update superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/CccsGrid.tsx Co-authored-by: cccs-rc <62034438+cccs-rc@users.noreply.github.com> * [CLDN-1968] Remove 'TODO's as they are no longer needed * [CLDN-1968] Changed a variable name, and condensed a few lines * [CLDN-1968] Modified a setState so that only one is needed instead of 2 --------- Co-authored-by: cccs-rc <62034438+cccs-rc@users.noreply.github.com> * Feature/cldn 1968 (#272) * Add buttons to expand and minimize JSON data as well as ability to expand and/or collapse all rows in a certain column * [CLDN-1968] Added expand button for full row * [CLDN-1968] Resize JSON columns * [CLDN-1968] Added new array which tracks JSON cell state * Revert "[CLDN-1968] Added new array which tracks JSON cell state" This reverts commit dabc3daee96cfbe39d9615837076e54736e518b6. * [CLDN-1968] Added ability for row level expand all button to track if cells are expanded or not * [CLDN-1968] Ran pre-commit hook * [CLDN-1968] Improved UI * [CLDN-1968] Update image tag for testing * [CLDN-1968] Revert image tag for testing * [CLDN-1968] Added multiple UI/UX changes based on QA feedback * [CLDN-1968] Added more UI/UX changes based on QA feedback * [CLDN-1968] Temp change to image * Revert "[CLDN-1968] Temp change to image" This reverts commit 57490bd71dbf14499f08e21e691933d6bbaf914d. * Update superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/CccsGrid.tsx Co-authored-by: cccs-rc <62034438+cccs-rc@users.noreply.github.com> * [CLDN-1968] Remove 'TODO's as they are no longer needed * [CLDN-1968] Changed a variable name, and condensed a few lines * [CLDN-1968] Modified a setState so that only one is needed instead of 2 * Update superset base image --------- Co-authored-by: cccs-rc <62034438+cccs-rc@users.noreply.github.com> * Adding componenets * [CLDN-2142] Finish Dateetime Picker and reorg control panael * added changes to align cccs-viz with upstream cross-filtering * fixed column sorting being reset on emit filters * fixed errors * [CLDN-2142] Adding validation for advanced type input * update base image * update base image * advanced type null fix * removed legacy time section * removed unused import * updating docker image * fixed bootstrap data * update docker file * [CLDN-2064] Fixed column header reset bug * fix column sorting not updating columns * updating base image * updating base image * [expore++] Fixing bugs with page reload * fixing build errors * Dockerfile update * CLDN-2198 updated formatting and removed change dataset button from cccs-grid * CLDN-2202 updated formatting and fixed time range validation errors * CLDN-2195 prevent running an emtpy query aggregate mode * CLDN-2203 make wording in date errors consistent with labels * CLDN-2205 make time range invisible when no time column is selected * CLDN-2206 Fixed formatting, labelling, classname, and disable selector values when there is no selector * CLDN-2207 show advanced type labels and not ids in selector dropdown * CLDN-2208 clear values when selector is changed * CLDN-2210 change button name to Swap Dataset * CLDN-2211 fix query builder to use actual values rather than strings * fixed npm errors * update image tag * merge fixes and tool tip update * CLDN-2209 fix incorrect dimensions after swapping datasets * Select All not working on swap datasets, default to no selection * fixed npm build errros * update base image * CLDN-2208 Mandatory to have a value if selector is selected and invalid entries disable run query button * disable time range when no time column is selected * change selector to advanced data type and values now saved in form data properly * fix refactor mistake and allow querying ranges of advanced types * added comments and removed debugging lines * remove unecessary tool tip and fix npm issues * validation in progress to prevent running query on bad values * update base image * fix for temporarily caching form data * readded tooltip * fixed tooltip, added order by * npm error fix * update base image * removed changes from hogwarts table viz * fix error in tooltip for adt value * update image tag * removed unused adhoc time filter * fixed import order * update docker file * [dataset explorer] pr feedback --------- Co-authored-by: Reese <10563996+reesercollins@users.noreply.github.com> Co-authored-by: cccs-Dustin <96579982+cccs-Dustin@users.noreply.github.com> Co-authored-by: cccs-rc <62034438+cccs-rc@users.noreply.github.com> Co-authored-by: cccs-RyanK <102618419+cccs-RyanK@users.noreply.github.com> Co-authored-by: GITHUB_USERNAME Co-authored-by: cccs-nik <68961854+cccs-nik@users.noreply.github.com> Co-authored-by: cccs-RyanK Co-authored-by: cccs-tom <59839056+cccs-tom@users.noreply.github.com> --- .../src/cccs-viz/plugins/index.ts | 1 + .../advancedDataTypeValueControl/index.tsx | 131 ++++++ .../controls/changeDatasourceButton/index.tsx | 73 ++++ .../controls/datetimeControl/index.tsx | 131 ++++++ .../src/plugin/transformProps.ts | 20 +- .../src/utils/advancedDataTypes.ts | 16 + .../src/images/Table1.png | Bin 0 -> 11146 bytes .../src/images/thumbnail.png | Bin 0 -> 1225 bytes .../src/index.ts | 27 ++ .../src/plugin/buildQuery.ts | 236 +++++++++++ .../src/plugin/controlPanel.tsx | 398 ++++++++++++++++++ .../src/plugin/index.ts | 67 +++ .../src/plugin/transformProps.ts | 348 +++++++++++++++ .../src/types.ts | 103 +++++ .../index.tsx | 1 + .../useAdvancedDataTypes.ts | 3 + .../src/visualizations/presets/MainPreset.js | 2 + 17 files changed, 1541 insertions(+), 16 deletions(-) create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/components/controls/advancedDataTypeValueControl/index.tsx create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/components/controls/changeDatasourceButton/index.tsx create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/components/controls/datetimeControl/index.tsx create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/utils/advancedDataTypes.ts create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/images/Table1.png create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/images/thumbnail.png create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/index.ts create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/buildQuery.ts create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/controlPanel.tsx create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/index.ts create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/transformProps.ts create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/types.ts diff --git a/superset-frontend/src/cccs-viz/plugins/index.ts b/superset-frontend/src/cccs-viz/plugins/index.ts index f8453be3c882f..916006072772f 100644 --- a/superset-frontend/src/cccs-viz/plugins/index.ts +++ b/superset-frontend/src/cccs-viz/plugins/index.ts @@ -29,3 +29,4 @@ export { default as AtAGlanceUserIdChartPlugin } from './plugin-chart-at-a-glanc export { default as AtAGlanceUserIDSasChartPlugin } from './plugin-chart-at-a-glance-user-id-sas/src/plugin'; export { default as ApplicationLinksChartPlugin } from './plugin-chart-application-links/src/plugin'; export { default as IFrameVisualizationChartPlugin } from './plugin-chart-iframe/src/plugin'; +export { default as DatasetExplorerChartPlugin } from './plugin-chart-dataset-explorer/src/plugin'; diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/components/controls/advancedDataTypeValueControl/index.tsx b/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/components/controls/advancedDataTypeValueControl/index.tsx new file mode 100644 index 0000000000000..eee9e5dcaa6c6 --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/components/controls/advancedDataTypeValueControl/index.tsx @@ -0,0 +1,131 @@ +import React, { useEffect, useState } from 'react'; +import { ensureIsArray, withTheme } from '@superset-ui/core'; +import { connect } from 'react-redux'; +import SelectControl from 'src/explore/components/controls/SelectControl'; +import useAdvancedDataTypes from 'src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/useAdvancedDataTypes'; + +export interface Props { + colorScheme: string; + annotationError: object; + annotationQuery: object; + vizType: string; + theme: any; + validationErrors: string[]; + externalValidationErrors: string[]; + name: string; + actions: object; + label: string; + value?: any[] | any; + datasource: any; + multi: boolean; + freeForm: boolean; + advancedDataType: string; + onChange: (values: any, errors: any[]) => void; + disabled: boolean; + description: any; +} + +const AdvancedDataTypeValueControlValueControl: React.FC = ({ + onChange, + externalValidationErrors, + datasource, + multi, + freeForm, + advancedDataType, + label, + disabled, + value = [], + description, +}) => { + const [rawValues, setRawValues] = useState([]); + const [validationErrors, setValidationErrors] = useState([]); + const [currentAdvancedDataType, setCurrentAdvancedDataType] = + useState(); + + const { + advancedDataTypesState, + subjectAdvancedDataType, + fetchAdvancedDataTypeValueCallback, + } = useAdvancedDataTypes(() => {}); + + const onChangeWrapper = (selection: any) => { + setValidationErrors([...validationErrors, 'Validation in progress']); + setRawValues(selection); + }; + + // clear selection on advancedDataType change + useEffect(() => { + const rawData = value[0] ? value[0].rawData : []; + onChangeWrapper( + (currentAdvancedDataType && + currentAdvancedDataType !== advancedDataType) || + !advancedDataType + ? [] + : rawData, + ); + setCurrentAdvancedDataType(advancedDataType); + }, [advancedDataType]); + + useEffect(() => { + const data = + advancedDataTypesState.parsedAdvancedDataType.length > 0 && + advancedDataTypesState.parsedAdvancedDataType.split(',').length > 0 + ? { + data: advancedDataTypesState.values, + columns: datasource.columns + .filter((col: any) => col.advanced_data_type === advancedDataType) + .map((col: any) => col.column_name), + rawData: rawValues, + } + : { data: [], columns: [], rawData: [] }; + onChange(data, validationErrors); + }, [advancedDataTypesState, validationErrors]); + + useEffect(() => { + fetchAdvancedDataTypeValueCallback( + rawValues, + advancedDataTypesState, + ensureIsArray(advancedDataType)[0], + ); + }, [advancedDataType, rawValues, subjectAdvancedDataType]); + + useEffect(() => { + setValidationErrors( + advancedDataTypesState.errorMessage + ? [advancedDataTypesState.errorMessage] + : [], + ); + }, [advancedDataTypesState]); + + return ( + + ); +}; + +function mapStateToProps({ charts, explore }: any) { + return { + // eslint-disable-next-line camelcase + colorScheme: explore.controls?.color_scheme?.value, + vizType: explore.controls.viz_type.value, + }; +} + +const themedAdvancedDataTypeValueControlValueControl = withTheme( + AdvancedDataTypeValueControlValueControl, +); + +export default connect(mapStateToProps)( + themedAdvancedDataTypeValueControlValueControl, +); diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/components/controls/changeDatasourceButton/index.tsx b/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/components/controls/changeDatasourceButton/index.tsx new file mode 100644 index 0000000000000..755d56fa218aa --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/components/controls/changeDatasourceButton/index.tsx @@ -0,0 +1,73 @@ +import React, { useState } from 'react'; +import { ChangeDatasourceModal } from 'src/components/Datasource'; +import { withTheme } from '@superset-ui/core'; +import { connect, useDispatch } from 'react-redux'; +import Button from 'src/components/Button'; +import { updateFormDataByDatasource } from 'src/explore/actions/exploreActions'; + +export interface Props { + colorScheme: string; + annotationError: object; + annotationQuery: object; + vizType: string; + theme: any; + validationErrors: string[]; + name: string; + actions: object; + label: string; + value?: object[]; + datasource: any; + onChange: (a: any) => void; +} + +const ChangeDatasourceButtonControll: React.FC = ({ + onChange, + datasource, +}) => { + const dispatch = useDispatch(); + const [showChangeDatasourceModal, setShowChangeDatasourceModal] = + useState(false); + + const toggleChangeDatasourceModal = () => { + setShowChangeDatasourceModal(!showChangeDatasourceModal); + }; + const onDatasourceSave = (new_datasource: any) => { + dispatch(updateFormDataByDatasource(datasource, new_datasource)); + }; + const onChangeWrapper = (a: any) => { + onChange(a); + }; + + return ( + <> + + {showChangeDatasourceModal && ( + + )} + + ); +}; + +function mapStateToProps({ explore }: any) { + return { + // eslint-disable-next-line camelcase + colorScheme: explore.controls?.color_scheme?.value, + vizType: explore.controls.viz_type.value, + }; +} + +const themedChangeDatasourceButtonControll = withTheme( + ChangeDatasourceButtonControll, +); + +export default connect(mapStateToProps)(themedChangeDatasourceButtonControll); diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/components/controls/datetimeControl/index.tsx b/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/components/controls/datetimeControl/index.tsx new file mode 100644 index 0000000000000..3d106234261ef --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/components/controls/datetimeControl/index.tsx @@ -0,0 +1,131 @@ +import React, { useEffect, useState } from 'react'; +import { SLOW_DEBOUNCE, SupersetClient, t, withTheme } from '@superset-ui/core'; +import { + buildTimeRangeString, + formatTimeRange, +} from 'src/explore/components/controls/DateFilterControl/utils'; +import { Input } from 'src/components/Input'; +import { connect } from 'react-redux'; +import rison from 'rison'; +import ControlHeader from 'src/explore/components/ControlHeader'; +import { getClientErrorObject } from 'src/utils/getClientErrorObject'; +import { useDebouncedEffect } from 'src/explore/exploreUtils'; + +export interface Props { + colorScheme: string; + annotationError: object; + annotationQuery: object; + vizType: string; + theme: any; + validationErrors: string[]; + name: string; + actions: object; + label: string; + value?: object[]; + onChange: (value: any, errors: any[]) => void; + default: string; + disabled: boolean; +} + +const SEPARATOR = ' : '; +const fetchTimeRange = async (timeRange: string) => { + const query = rison.encode_uri(timeRange); + const endpoint = `/api/v1/time_range/?q=${query}`; + try { + const response = await SupersetClient.get({ endpoint }); + const timeRangeString = buildTimeRangeString( + response?.json?.result?.since || '', + response?.json?.result?.until || '', + ); + return { + value: formatTimeRange(timeRangeString), + }; + } catch (response) { + const clientError = await getClientErrorObject(response); + return { + // keep labelling consistent in error messages + error: (clientError.message || clientError.error) + .replace('From date', 'Start date') + .replace('to date', 'end date'), + }; + } +}; + +const DatetimeControl: React.FC = props => { + const [timeRange, setTimeRange] = useState(props.default); + const [validationErrors, setValidationErrors] = useState([]); + const [actualTimeRange, setactualTimeRange] = useState(); + + const [since, until] = timeRange.split(SEPARATOR); + + useEffect(() => { + props.onChange(timeRange, validationErrors); + }, [timeRange, since, until, validationErrors]); + + function onChange(control: 'since' | 'until', value: string) { + if (control === 'since') { + setTimeRange(`${value}${SEPARATOR}${until}`); + } else { + setTimeRange(`${since}${SEPARATOR}${value}`); + } + } + + useDebouncedEffect( + () => { + fetchTimeRange(timeRange) + .then(value => { + setactualTimeRange( + value?.value ? `Actual Time Range ${value?.value}` : '', + ); + setValidationErrors(value?.error ? [value?.error] : []); + }) + .catch(error => { + setValidationErrors(error); + }); + }, + SLOW_DEBOUNCE, + [timeRange], + ); + + const headerProps = { + name: props.name, + label: props.label, + validationErrors, + description: actualTimeRange, + hovered: true, + }; + + return ( + <> + +
{t('START (INCLUSIVE)')}
+ onChange('since', e.target.value)} + disabled={props.disabled} + /> +
{t('END (EXCLUSIVE)')}
+ onChange('until', e.target.value)} + disabled={props.disabled} + /> + + ); +}; + +// Tried to hook this up through stores/control.jsx instead of using redux +// directly, could not figure out how to get access to the color_scheme +function mapStateToProps({ charts, explore }: any) { + return { + // eslint-disable-next-line camelcase + colorScheme: explore.controls?.color_scheme?.value, + vizType: explore.controls.viz_type.value, + }; +} + +const themedDrillActionConfigControl = withTheme(DatetimeControl); + +export default connect(mapStateToProps)(themedDrillActionConfigControl); diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/plugin/transformProps.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/plugin/transformProps.ts index c35777b1dccd0..787772931ebac 100644 --- a/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/plugin/transformProps.ts +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/plugin/transformProps.ts @@ -33,6 +33,9 @@ import { DEFAULT_FORM_DATA, } from '../types'; +import {rendererMap, formatIpV4} from '../utils/advancedDataTypes' + + export default function transformProps(chartProps: CccsGridChartProps) { /** * This function is called after a successful response has been @@ -176,22 +179,7 @@ export default function transformProps(chartProps: CccsGridChartProps) { }, sortingColumnMap); - // Key is column advanced type, value is renderer name - const rendererMap = { - IPV4: 'ipv4ValueRenderer', - IPV6: 'ipv6ValueRenderer', - DOMAIN: 'domainValueRenderer', - COUNTRY: 'countryValueRenderer', - JSON: 'jsonValueRenderer', - }; - - const formatIpV4 = (v: any) => { - const converted = `${(v >> 24) & 0xff}.${(v >> 16) & 0xff}.${ - (v >> 8) & 0xff - }.${v & 0xff}`; - return converted; - }; - + const valueFormatter = (params: any) => { if ( params.value != null && diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/utils/advancedDataTypes.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/utils/advancedDataTypes.ts new file mode 100644 index 0000000000000..0165b55f025b1 --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-cccs-grid/src/utils/advancedDataTypes.ts @@ -0,0 +1,16 @@ + +// Key is column advanced type, value is renderer name +export const rendererMap = { + IPV4: 'ipv4ValueRenderer', + IPV6: 'ipv6ValueRenderer', + DOMAIN: 'domainValueRenderer', + COUNTRY: 'countryValueRenderer', + JSON: 'jsonValueRenderer', +}; + +export const formatIpV4 = (v: any) => { + const converted = `${(v >> 24) & 0xff}.${(v >> 16) & 0xff}.${ + (v >> 8) & 0xff + }.${v & 0xff}`; + return converted; + }; diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/images/Table1.png b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/images/Table1.png new file mode 100644 index 0000000000000000000000000000000000000000..1c9791782465aaee9c3d6d1f55120ac893cceea7 GIT binary patch literal 11146 zcmdUVcUV(dw|4*q8M-JSErJRJ479uuUnvAG41T7%*FZ(x!t=n*5tplmS3#gs6x(-) zqrg3@_sx5LAP{FO?VkbbmFENkT?Ff0zj`OgmXc~5JAE@C+CR51LNK2#$a4aoBV+Hy z0*|;Q{cF_ZQ+`{HcY(jWIiJYj$SEh}s7N$a5Z{^8SvmSSF@GTYw{ey*(wKcA8n z4`yUmBX9=fSO@u$Y0asvLxO z&vBA>;-%GT=v&V!X=-F>f>w0>>*rv*Gydfs^Wd>^@Od0{@ox z`rT}OH?jHT++p@ExreHfMfGaFoKa2)c0ZtaO(q?v9`1KKt{bD8FCUJI^n@e4b|K9B z+u!}x?+zc3dmOF8G^wGaFC*>E`#sGs{IX`q#>Krmsq>{9)!piI;c%NtLecHU_O-c_ zRBL$SQqVnQY4>b;E@2`JAx-emFlryCCWjQ*W$&+e@Hz*>Z$Jq(ferXg%YM7;9UEWI zOoS@o*OWKVoY;OsY(ee8f^<*iL6MVn{ioXCajUN==b2p$843?6EbQ?|w7P~+X2|}< z;Wb)HX)p0ck1(Yc$cXP`*^5lXp?6VogQXK?1o#7V0oPEtNsixsv#baK6+@q)H6+#=0nlUMF7g@2KBC%Q=}UDDPiVCr%csxrlmBBrogTV*4^#TkujGK&9-x;U3Z}T9E1Hv=Sp%}}whId~z z9v5fGwN~a0e)w+j1v}QUYtV4R|BnS9sk}wmQ>I-0wP5#Pp{Ib*n>>D!tOsvi^RnI# zzdSeYNLl7Yty>ynD|UPLlnm_Z%Y7S{2D$i2eYzT-2VPy2Y{@d2mY?zeM&_~=!{yAu zG&{Gm8qM*Fd{ie~R7N~&SlIHKKNS07i;H{qu@@_>A9|;_G>D39tI4o;8t0fZ-@k5T zi{F0L4Mupi-6Fd2u8fi?8t~00Dpo9eS44}f6lWJI-!--`j$J#16`fM@Bis^=o37}W zE1KOe2vsxFg@z{zx@XO+za^KfQC)2@_?i8OVC;C>fg+&K<^Y$O5MlvX24~1J)4^(T zqBF=Sr0dSFd*lY`%}xS;@i~^5Ez5f;JN1sCLFoe50p%j>RGSgj!a^q~3s>rl$B&cV z5nMCdN9LUH66N@LXy^LRRAq}`@IB3*yg|zXMTX}0Uo{>Tr^(HcwVKuOBZst2OD*F!)C@lFC%+? z3Gex)GQU~Q`pu}(5Y0Bn@6Qd@CPRE;ox^Mo>phmUHFi%HS+K8dyJM>Nw>SM2?y~s# z-By`(tE{@5;ZLo)Ts>AxV2AD2+zZB(`TRVJk13crNG{I)rCC1|^+9T*FjzCWbJ+)u z32Zh?0e2y^cSq9V0na~9y3UfzAO#7u5u zgEIWs+!fEi-p*3b4Mp^?FLkCIsaqV`?jYRMgjSwPPobvoSdLsj_siGloaUeX3k+L| z-&b=@6f8KGa?bMA;tRP5-7X!K%eK)67wFFZUfnAs1}X0(aJQU#65K=Rts5Uy@3y_z z*d4@S-T>}8NtpxScvcpYVN-# z^3`l>|3!2Ccj7SUqHV7KcVfTe{K)HNpD@JyDts?NOR4cjNDMXF+z#VNJPDUf8^|n_ zY~4>XC{hsQODEt{0@Rm269!^bdrnYBuHf6P4_5;M3A-#s;(a82ZgkT;jm@nS_`)(b);ncP`#IY&Kuc&6K_uU{k5dpr~~USFuvnrM2zr89S_{ zrufhXLuI$0$x#8f9tn3N-;}Bw93jv1F*6dISp9w7p>q}ky5^eFlxc-ilL2>%4?l+7 zCL2GBF=4K-^d7ZFvq4V#x;O<1vk+@uMnQh|%$x9d@;uxc^uCh7Bbw12Eb{SyM9wNi z)j+O$?A&fVSdS35)6b->nzQK;b)1bu@O3Z^Jj(yRwg@-ye>~HHF zV%M(KA6y`9j%Rc>jn)ihfDVAw$g)F2Ss`a0PKc!R=Z~Jdz-OxA`?T8ZNF5($O9JA5 zq>h+HnR;*cj?2Ff&*S%+cX+dS8HZUi2I*G3bnd-^)7zUc=WM7+`PdSvB+mi+?M~ycKq zm7CJ5T_Y1a%`dmBa|5WzEj$j@No6hJs6bcYDT{QASrm}Y!^G3Gz!C!<4f?~p+(G}NmmA&Cp$7*{Kt9NG_vce4ok%c+Yj~{Ofdav?NmX z0`1`scQU&5V{o83EABXb7+PXUdi$?lzji(p@CuqpmUDDpqB$qbB#Z7We>Rf@Nlj|P z$*p3$^Q*N`gr^1=yA_>#@ZB=hHw@#rzH1fEzFlDMXa`1VkCO6tkEgH01V`GFKQ#)H z=O*>k`K(oAmnY5EU&h#|%q3#KS@OjCPYe5;xkX!DSsXmnUBiV51AcAXmDtp%KG!b? zf>IuuK3HvgJ}B79!61t_zU@`H?jso$qMV)_?U=)v5IYTdgX#*33qA_y$oVcRg}1eD37s#=pP@>J=pZw$wWB8a(0JA8N>fuVH{J=I<)+_|>eC(kJ2 zssEg>Ps;NwfO3{QJNo;|+Ev($(fV)U$TaWh_nU%b{%Kza=ghC=Q_QEAKEVuWxrraz%HlGzoSz`H^L<{gOMYfj=PxqTqd>ot$QuXNGu{D7ztxyzl@RMf34(Xx6#ae`Q5~x}{v#Hwo_R6FsTBT%hh<_&(cop} z)|x=Q-_SiwX^?fwn6?!{hWF4G1LvwfwD6w~8jd9dT)=&nm+krUYsWe1RZeXDk9ZB( zdGA%4GJh07A~cNvVf=441Hhq*(?BO8}|8#)h!eC)b$ zV$rF9_8b+FSVN7r6CPRNoY7{XCp?WcY_XO1M-aZEFUw+E5^%sguv(HVHXe)~EH;Wa z_n(_V!03sYvDD~z^8tg=@H^1?KI^^pazA@J*L8hNxMi7YWD55_x@KN*&%qO(1Yw>) z0-v>av`+4H+mcmHos%QbxzHwe^oTpVaSOewA36O)yQ`VTJ8GiQ%5s z-VFBzDM?Vi3+*>k=t!kFma25In##ES z`8&gzj#J|opo})Ufo2_dVA@kxT%tKOL}_--N7{I_)kCq@4{UrRHgJ-`b1m!<>zX4~ zb!J;B)Qz&zdwaoI;`*l)rk`e_-lkOSos-0UfYX9VI0Pn!J_&)vJ(37qEn|(p%6ieF z9C?wv;>*LPc%{naW7wT;V{K`h?kt%N5&IyQk2M}%Tji6)BA)LT@74h61ud#o{t6C# z{@GNV$p$U-DK3H?{c`ZGi)B#j+ppEnZ4!g}<{EeB5_;Ni1*% zLJKpZB{wa+=arkHjk~l3>f_1jcoYqG5KAzrey^I7e}u6nb;XQ~J*)$6lSz)FDta<0 zz$jcJ%dN8AO;b}T1LcsVS?K}f8l(80Tt_Q5Y=?C@OnChBWoONNg1Rsi>#f*?EH!AJ zjH1V4GAGg1;wJXP=FHVou0Grvigxwb5R!J`1vmH7gx5NtK$oF;*7|BH0)t&`G>BNr zw2>SjZU$+abHO%*JoWwnmCsiRl-~?AIO8%=NBJ=cR}}B+5Afr?KzI@Cw!*JH-jq(C zk!?LhgXUdGF7-`#E5~_YjoS>5)RkywjbHd2F)YYz&%ZI0rj3jfOu^*%^0W}&NN=q% z+t2uf>0llAh3WQw68mkl{L(f{aU6xXHeIt~ z5&k?J&|7UdOg&_Q<)I39f8e?FGCv-_O+l-ob3hu(LDdYW zI93!t2T-S}qXi$cDj6Inf$F4b>IZ7sp{;m-|46*6Dc8E&R&=ZC{FwwiO-bLxAJv!V zp`1qMV<1}k*z$merb{K2Q3tM(!^%+?BVvKC{h4Ob)mr@3bx26g<!ma&YL(r(VuYXw!B z3W2yy%Yh>QpPJhWj@HdBYtkq=pB$Q`21|Oa@raQ5*J9$|i5&lM)Lu=TjZxR6x0?*H z^5~kJ+!-&L=+ z+6`y=iY@v_w07G?ilD(8v2re3AjmW(^!=+U~f}^rC*q~JjGks5=+-Q?P=1D&vMZ$pWCB^MM#pM4n;4ol$v#r&B6ncnbWhdm)wq$ z+C)`(!XWv>k{YYLXz_u{xz%Fp4{Se#2r=9G3cF9LVMMefmr|c4v(B=4og(GQA0s!2 z;@Z<{8jOb0>ZQqhE+GmDvyqlK^0!cb`qFrw{!y3+(fxu3BLB4l{(D9XWKqQRZ7>ZW zYD{S%4oI`8Tl;@$__ri)|GkhbFLbEtmFQNnz4_JP+Q!XJZ8uHf*seJ*M@ZJJg=|?u zdP)zrlsc)W=JIaKTUO%=mkkH0k~>!6aA$b4r!~%Z5j9JmE~O~+YTh@Xr(!;g z#R^Aw=DHyv5uT7(0%FHWvVtpP?=>}U&!|Y@0l0nlks;%caxpxjXzdl*9UX_fh?Fk6 zWH>vxBI|kL7%ZvqXkAl`)?7i&!F`catu{G`I*uo+sZ0Iod1axfASOC&@saS%)sqdr zJ`YOUbY{e@11*lZRT9+O8}p7l7GU1tBr3ipGz(1|`$nR_YC0I?${T^F6zNJG=d`g< zLyFYZ1er^A`*L?u{I0iIk`N)s1E;f!EGpzJx8Olib3$&dgsHt~pRZdzLF?yYtFL-3 zO4eE#|ECauo`?z5rzQl}?&B+6GBKllY^|4nMS~mUVsEF6` zSQm{xY+wnyqgdW&l)=nuR&kVnb{NNXF#S^m>03D4@BcS)rhk^a(bB^S{~xsy@bh0% zUVqQw|H7lexL}F>nJU7Mv9sU%t}x5wR#675>!Kr8$F%*v7%?Yh=H&p`l$YJR6r`3Y zII5I5d%|tvtV@QL>+;-%UB3yS;HjKz+05TG7LW1jRl0A0TG!eZjBMLuns@|6k~-!( zZrnShv!;Cd2!kXev9nf%i>u@F39W2X{i6;HH~j?`ZW-+i3~A`W5$;gQ9`-VcwLRS2 zr6BO$L2tW@vIL`Ly{GV?|Jmhc0Y=nZKs#tf`-gEu(9SF-0^XH6%2ER(_RP4Xf^$An zW1o>H!`MSgWb4`v`l2G-V;4poTX+1}nHIkgU&1KHk`Kb#<%K;b>cW3Q4f@~MdVmgN z_YVD$+H)`!kFKS&*U7b*OML_(zYOSGMN_lnSiN;xA{*somUg>StT*a2dMaB$%ev!S zGc`+I8=c{O0md^b5o_`-6k2{Vt|iHg+c<;tEs0%OWgf~KG;QkFM@K%f9-9j{Qp$e) zJLc5BSmDvuO)hY8ySMcI1sr@+oLul-7+}t%(Ey%xLtytMQl-)zTH=x3f=N$QN-_sc z^({cxyLd>aZy$M_$>|ij?YCp0$i}sD@GM;5nlE_iRovGxyY8d+3}>I9VbUHmZC4g# zqWw3k_kZMIf1w2a37ey91&DgE!?y7+(1x^=j zjwd@ltG-LC=2MQrCigk0-*%F<_|titd9Nn5z9?%iBdP+aEQX^pK7hfZVmq&jl<1MK z@v)(GJHV?@xcvPQ)rKGOT$uC@Ihztl5-3cnHF0d4k{c7Q@ZVTz;UsF<@RC%gJ$W*h z-j!)bgKyGoPnB7FJ-&cG~MWVA|idaT4u=mKL$9JFj96 zQ`mYBSAFdCjZybw-zjGo#vXG=_jptPh+op_xn_{OZ}*|@2o3d6~BivRJ~!9 z68&o&!lNT9=CiK>iAJy2!}uqnPMhCj=_ujD28lA;b?Yh~ZQ%e?_KopOWTdf`GKLaS z@<^ffnx3t~%v+4{l%GFyD7qLqBHj!W{)&`SLr&{fy{ktO- zWJl9zy7T<@{#R$S6VuM{ZHpf}n-JT&&(rRAs-e#_Ot5TZ^%1)}wa3@f z{*P|jN9$HZSeITq+`6qW9JtVT4M#9M7PuO!^rRLZ_w@3qBdUM^$nkc+;KqJsQ6?Km z)KjXrXtORMy?=9jW}uNz$%oFOhQHKGlKs0%rskGbKr8VukkIl zQ{`ON6X>x-lSeD2Mc}TylbGesGGhkM7{CC+A8BEU@=~`8az1U?HJ_ugb*!7eEw7y2 z7qkTW>DL^zrGf(t$`A4jS&7{XIUHWCL!2JD3glxov|?`?Od67i)^V#GIKGg3NLuve znfoI3(@N>9EApo``m6v61}ciJj=_|}ux)kq3@y&DG@6*mQnzzkHz4tgZn23e z-c&P!KQ5@D6HI>A7MwBAgd9Z_)&ZIqQVEZI5X6QhD>;^CywDQed)>t3MGI3*bR)JO z|M%u&|HN08s>lE0JpY?S-Ct!>nTY-QD#AuxiBGqRQP{23OgUltc2HGx?v%ebAv{IV>q*D@w0Xw*Ew43qhK zqRk*UCv-vOT-2jPiD7^UY+eZmHio*?SOT4`xH6(mB={#+{OlxyA(c;p4L4A&sT+4x zVr8P_L);0^v+?|v;69f>HZ+>G`B{>yyzwJ*v0dsc+a5KyNfH|+5Xq$lYzkAbMrDPa z_BcNh!M74W9pty~V+C$LqB_<@CwO|a2r|F-Go+5pT#Hdw#rHcg;1;4RM@mPgGBi|f zf?Ihxzn@fc^~qf--Axve-G8Rh>OteM+92$(`=)Og8}x3 z)Qs>na~AWglQ8(l`}=G#htmYU(GocYS9t~LPnTLSUo*240^V2cbmFv>SE__tS8qrO zwLSp%y3`ob!g^3(gbe#wEdnkOaDRvJJZWHQ%!&>(X#;(>zwjZNw5!EhH#Og;Z5Y(r zzJ_1Vm{g?id!!vzeGm~Il6>k@4YV??+TO$GU4k6FDr!})oOBZ)#4UfxN!tne$3QL- zMh}t5^`OM|*q^rAQ4ZHVjzp)`?7VWjtuS!>Myr|v{=-K0_FB;HVSelOop7*uGGbA1 z&sDHvUSTYmro}NW`j-D#2JPnmb5hQXxgsNc?_r-J!RW_1gLmw%Vm0xThETFU$B%;O zH|)ATVXrS0<+wlJcjfLV?=(~-WikiEm}q9jpZo6oo8LQkhlFG2s_ImyYV#A?lb^tS z9vc0@NA$QyC@&-1w2CeSA5t&EnNxDvUEx7wChOgr0a;vQYRbAC=gg7dhcfWc&~6`# zXTZ)Vclpl8*Gm!;FgLP38;tl72w7QQEkIkhhPQ3f*gr&!3>|T35FIUr5A0ndYwxjF z0RhzsFh;+t1d+N|B`bL52OeU4Y{g>^3`uCh?V&VCT5HzJ1xd98CFU8&A-0YwdpZ6;jr$;DR;2++oOahwG%fO)0!sQQOzAa=1gVk2f_jNbrk=PA_gW47o~8SQByV= z5u9fMM+pe+uh3Rr(RF8hCa-3?#RF#^)n$9wsP7M%AV!H8Copcv|Iv6$tXo`w}yQB z*M@OM=tvzWG@E81xJO?SJp4>BGA&c;Ky1h5%5~+H>`PJQHale~lERP%Z3BtEG3wjP z@&Kd0e=l5$5y$Ix-Drz(b6-h1Q=>9qh+$?5EZP~IqsFPEBa$*RR=g<&d@JoB3Xzex z(~EZ1(Z^YH`j^Qd*3PAk*>bl5>D z^wh*3V+U2zw_cuw7da@>iM*Dre{trNo1Q8y3I8~8koJEZST*ebt|`hWm&lmIy-Z7~ N^mL4_7hSV|{2xG0(G>sy literal 0 HcmV?d00001 diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/images/thumbnail.png b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/images/thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..8a06b78ed3017822e8fd6fafb55fdab9c3d28e4f GIT binary patch literal 1225 zcmV;)1UCDLP)@OD00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1YSu*K~!i%wU}FI zR7V)cXA-m0F4C;7DOR(j;(B?I_~0eJ*$TCgSZolZ>BW5!6a^b7eTaEX!3Sj*T6#+% zJ_t7LgMuj`63Wt|i8l~Ki`2xLc#AIX+L*N@+nUbI`F8i5nKS3i?9Tr9XEWbqzVG{= zIkS6~V6>wPIl%DSIM`U_D(2EAQo2Io8kM1{%9ZBDH7Y~GP7{PhX2eQsh)cIg*l8jz z*;`z$L2-?2ax<+Zp`tNyjcgK+ho*#z#>6$U$#tj6JU1hkvz(vEi%>yd7&UjZw}=xm z&-mn>RqI#0W{u5~xG*+)e;NW!FlzZ6h7Q1V*UmLfD;Ha1vvl|WIdUfrfhLkOUAPyO z#o1I;!G=?kPY*}M%D{DWRndb(7`=BKs_>9Mqps#?)5?$)8{}5%#g&DN=4%!}tVZ(Z zo2e=KHt_w(Gf!!b8Q@Cc;bN3B922)-$z!L<3wt$3S&k{-4WnHrV8#V%I=Haf3d$s0 zw`j|@Xc>-Ca^UyW)N?W*rcYW^E|}ZthUn4_n;U8ZWnF)A;E&W}YTENm=WmCSxLliz zIA%aEj9TL`;|^I=gdCNiX78F%uyJ!F5Gegr6u9`yZEL5A>4Y^U!C`v)no!5v^$;j9 z`f#Rua3VYVAH)tEm9^0SKCR5?!)*89WOnu?^CyTat0TXM3ztAAm7tCyy%n*7hNbO7uX$8fW49Dw#tgSB1F)1MQ zQV>RadmukgQzQtZ0%VyRf2SXO7Cu|LV}#xxKLR5a@-8si_dN{r^nLhdFhGut4?|jFN3Q)(F)7jiSpKFPooQjCmcYP!JFf=yPb2*cpn->!EvR{B% zvHBQ&&o;p^my<2fB%_-SWfcwwKqZ$2t1%x^dP2B!7xu>d%eswalHG} zgW-(Q6QpBfdt&Vl2z=1Y2_vegd%~LO*x$20VpvHEL{i zF=~N-=w%kG9mmw&AR=iY4RS`00000NkvXXu0mjf``1N< literal 0 HcmV?d00001 diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/index.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/index.ts new file mode 100644 index 0000000000000..7a3040d0f7605 --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/index.ts @@ -0,0 +1,27 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +// eslint-disable-next-line import/prefer-default-export +export { default as CccsDataSetExplorerPlugin } from './plugin'; +/** + * Note: this file exports the default export from CccsGrid.tsx. + * If you want to export multiple visualization modules, you will need to + * either add additional plugin folders (similar in structure to ./plugin) + * OR export multiple instances of `ChartPlugin` extensions in ./plugin/index.ts + * which in turn load exports from CccsGrid.tsx + */ diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/buildQuery.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/buildQuery.ts new file mode 100644 index 0000000000000..80a02fab53ad7 --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/buildQuery.ts @@ -0,0 +1,236 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { + buildQueryContext, + ensureIsArray, + getMetricLabel, + PostProcessingRule, + QueryMode, + QueryObject, + removeDuplicates, +} from '@superset-ui/core'; +import { BuildQuery } from '@superset-ui/core/src/chart/registries/ChartBuildQueryRegistrySingleton'; +import { CccsGridQueryFormData, DEFAULT_FORM_DATA } from '../types'; +/** + * The buildQuery function is used to create an instance of QueryContext that's + * sent to the chart data endpoint. In addition to containing information of which + * datasource to use, it specifies the type (e.g. full payload, samples, query) and + * format (e.g. CSV or JSON) of the result and whether or not to force refresh the data from + * the datasource as opposed to using a cached copy of the data, if available. + * + * More importantly though, QueryContext contains a property `queries`, which is an array of + * QueryObjects specifying individual data requests to be made. A QueryObject specifies which + * columns, metrics and filters, among others, to use during the query. Usually it will be enough + * to specify just one query based on the baseQueryObject, but for some more advanced use cases + * it is possible to define post processing operations in the QueryObject, or multiple queries + * if a viz needs multiple different result sets. + */ +export function getQueryMode(formData: CccsGridQueryFormData) { + const { query_mode: mode } = formData; + if (mode === QueryMode.aggregate || mode === QueryMode.raw) { + return mode; + } + const rawColumns = formData?.all_columns; + const hasRawColumns = rawColumns && rawColumns.length > 0; + return hasRawColumns ? QueryMode.raw : QueryMode.aggregate; +} + +const isRange = (data: any) => { + const v = data.hasOwnProperty('start'); + return v; +}; + +const buildQuery: BuildQuery = ( + formData: CccsGridQueryFormData, + options: any, +) => { + const queryMode = getQueryMode(formData); + const sortByMetric = ensureIsArray(formData.timeseries_limit_metric)[0]; + let formDataCopy = { + ...formData, + ...DEFAULT_FORM_DATA, + }; + const { percent_metrics: percentMetrics, order_desc: orderDesc = false } = + formData; + // never include time in raw records mode + if (queryMode === QueryMode.raw) { + formDataCopy = { + ...formData, + ...DEFAULT_FORM_DATA, + include_time: false, + }; + } + + formDataCopy.adhoc_filters = formData.adhoc_filters_no_date_default; + + return buildQueryContext(formDataCopy, baseQueryObject => { + let { metrics, orderby = [] } = baseQueryObject; + const postProcessing: PostProcessingRule[] = []; + + if (queryMode === QueryMode.aggregate) { + metrics = metrics || []; + if (sortByMetric) { + orderby = [[sortByMetric, !orderDesc]]; + } else if (metrics?.length > 0) { + // default to ordering by first metric in descending order + orderby = [[metrics[0], false]]; + } + // add postprocessing for percent metrics only when in aggregation mode + if (percentMetrics && percentMetrics.length > 0) { + const percentMetricLabels = removeDuplicates( + percentMetrics.map(getMetricLabel), + ); + metrics = removeDuplicates( + metrics.concat(percentMetrics), + getMetricLabel, + ); + postProcessing.push({ + operation: 'contribution', + options: { + columns: percentMetricLabels as string[], + rename_columns: percentMetricLabels.map(x => `%${x}`), + }, + }); + } + } + + const moreProps: Partial = {}; + const ownState = options?.ownState ?? {}; + if (formDataCopy.server_pagination) { + moreProps.row_limit = + ownState.pageSize ?? formDataCopy.server_page_length; + moreProps.row_offset = + (ownState.currentPage ?? 0) * (ownState.pageSize ?? 0); + } + + const { datasource_config } = formData; + formDataCopy.datasource = datasource_config || formDataCopy.datasource; + + const { advanced_data_type_value } = formData; + const { advanced_data_type_selection } = formData; + let filter = []; + if (advanced_data_type_selection.length > 0) { + // in the case of ipv4s sometimes they can be ranges and not simple values + // this will be handled in the advanced data type definition in the future + // to avoid this complex logic + let simple: any[] = []; + let range: any[] = []; + advanced_data_type_value[0].data.map((d: any) => { + if (isRange(d)) { + range = [...range, d]; + } else { + simple = [...simple, d]; + } + return d; + }); + filter = advanced_data_type_value[0].columns.reduce( + (arr: string[], curr: string) => { + const new_arr = [ + ...arr, + ...range.map(r => `${curr} BETWEEN ${r.start} AND ${r.end}`), + ]; + return simple.length > 0 + ? new_arr.concat(`${curr} IN (${simple.map((s: any) => `${s}`)})`) + : new_arr; + }, + [], + ); + } + const queryObject = { + ...baseQueryObject, + formData: formDataCopy, + orderby, + metrics, + post_processing: postProcessing, + ...moreProps, + extras: { + ...baseQueryObject.extras, + where: filter.join(' OR '), + }, + }; + + // Because we use same buildQuery for all table on the page we need split them by id + options?.hooks?.setCachedChanges({ + [formData.slice_id]: queryObject.filters, + }); + + const extraQueries: QueryObject[] = []; + if ( + metrics?.length && + formData.show_totals && + queryMode === QueryMode.aggregate + ) { + extraQueries.push({ + ...queryObject, + columns: [], + row_limit: 0, + row_offset: 0, + post_processing: [], + }); + } + + const interactiveGroupBy = formData.extra_form_data?.interactive_groupby; + if (interactiveGroupBy && queryObject.columns) { + queryObject.columns = [ + ...new Set([...queryObject.columns, ...interactiveGroupBy]), + ]; + } + + if (formData.server_pagination) { + return [ + { ...queryObject }, + { + ...queryObject, + row_limit: 0, + row_offset: 0, + post_processing: [], + is_rowcount: true, + }, + ...extraQueries, + ]; + } + + return [queryObject, ...extraQueries]; + }); +}; + +// Use this closure to cache changing of external filters, if we have server pagination we need reset page to 0, after +// external filter changed +export const cachedBuildQuery = (): BuildQuery => { + let cachedChanges: any = {}; + const setCachedChanges = (newChanges: any) => { + cachedChanges = { ...cachedChanges, ...newChanges }; + }; + + return (formData: any, options: any) => + buildQuery( + { ...formData }, + { + extras: { cachedChanges }, + ownState: options?.ownState ?? {}, + hooks: { + ...options?.hooks, + setDataMask: () => {}, + setCachedChanges, + }, + }, + ); +}; + +export default cachedBuildQuery(); diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/controlPanel.tsx b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/controlPanel.tsx new file mode 100644 index 0000000000000..26269084b8ece --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/controlPanel.tsx @@ -0,0 +1,398 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; + +import { + t, + QueryMode, + ensureIsArray, + validateNonEmpty, +} from '@superset-ui/core'; +import { + Dataset, + ControlConfig, + ControlStateMapping, + ControlPanelConfig, + ControlPanelsContainerProps, + QueryModeLabel, + sharedControls, + ControlPanelState, + ControlState, + formatSelectOptions, + ColumnMeta, +} from '@superset-ui/chart-controls'; +import { StyledColumnOption } from 'src/explore/components/optionRenderers'; + +import { bootstrapData } from 'src/preamble'; +import AdhocFilterControl from 'src/explore/components/controls/FilterControl/AdhocFilterControl'; +import ChangeDataSourceButton from '../../../plugin-chart-cccs-grid/src/components/controls/changeDatasourceButton'; +import AdvancedDataTypeValue from '../../../plugin-chart-cccs-grid/src/components/controls/advancedDataTypeValueControl'; +import DateTimeControl from '../../../plugin-chart-cccs-grid/src/components/controls/datetimeControl'; + +export const PAGE_SIZE_OPTIONS = formatSelectOptions([ + [0, t('page_size.all')], + 10, + 20, + 50, + 100, + 200, +]); + +function getQueryMode(controls: ControlStateMapping): QueryMode { + const mode = controls?.query_mode?.value; + if (mode === QueryMode.aggregate || mode === QueryMode.raw) { + return mode as QueryMode; + } + return QueryMode.raw; +} + +/** + * Visibility check + */ +function isQueryMode(mode: QueryMode) { + return ({ controls }: Pick) => + getQueryMode(controls) === mode; +} + +const isAggMode = isQueryMode(QueryMode.aggregate); +const isRawMode = isQueryMode(QueryMode.raw); +const isTimeColumnSelected = ({ + controls, +}: Pick) => + controls.granularity_sqla && !!controls.granularity_sqla.value; + +const queryMode: ControlConfig<'RadioButtonControl'> = { + type: 'RadioButtonControl', + label: t('Query mode'), + default: QueryMode.raw, + value: QueryMode.raw, + options: [ + [QueryMode.aggregate, QueryModeLabel[QueryMode.aggregate]], + [QueryMode.raw, QueryModeLabel[QueryMode.raw]], + ], + mapStateToProps: ({ controls }) => ({ value: getQueryMode(controls) }), + rerender: ['columns', 'groupby'], +}; + +const config: ControlPanelConfig = { + // For control input types, see: superset-frontend/src/explore/components/controls/index.js + controlPanelSections: [ + { + label: t('Query'), + expanded: true, + controlSetRows: [ + [ + { + name: 'datasource_config', + config: { + type: ChangeDataSourceButton, + label: t('Jump Actions'), + description: t('Configure dashboard jump actions.'), + mapStateToProps: (state: ControlPanelState) => ({ + datasource: state.datasource, + }), + }, + }, + ], + [ + { + name: 'query_mode', + config: queryMode, + }, + ], + [ + { + name: 'granularity_sqla', + config: { + type: 'SelectControl', + label: 'Time Column', + description: t( + 'The time column for the visualization. Note that you ' + + 'can define arbitrary expression that return a DATETIME ' + + 'column in the table. Also note that the ' + + 'filter below is applied against this column or ' + + 'expression', + ), + clearable: false, + optionRenderer: (c: any) => ( + + ), + valueKey: 'column_name', + rerender: ['time_range'], + mapStateToProps: state => { + const props: any = {}; + if ( + state.datasource && + 'granularity_sqla' in state.datasource + ) { + props.choices = state.datasource.granularity_sqla; + props.default = null; + if (state.datasource.main_dttm_col) { + props.default = state.datasource.main_dttm_col; + } else if (props.choices && props.choices.length > 0) { + props.default = props.choices[0].column_name; + } + } + return props; + }, + }, + }, + ], + [ + { + name: 'time_range', + config: { + type: DateTimeControl, + label: t('Time Range'), + default: 'Today : Tomorrow', + resetOnHide: false, + mapStateToProps: (state: ControlPanelState) => { + const disabled = !isTimeColumnSelected(state); + return { disabled }; + }, + }, + }, + ], + [ + { + name: 'groupby', + config: { + type: 'SelectControl', + label: t('Dimensions'), + description: t('Columns to display'), + multi: true, + freeForm: true, + allowAll: true, + default: [], + canSelectAll: true, + rerender: ['advanced_data_type_selection'], + optionRenderer: (c: ColumnMeta) => ( + // eslint-disable-next-line react/react-in-jsx-scope + + ), + // eslint-disable-next-line react/react-in-jsx-scope + valueRenderer: (c: ColumnMeta) => ( + // eslint-disable-next-line react/react-in-jsx-scope + + ), + valueKey: 'column_name', + mapStateToProps: ( + state: ControlPanelState, + controlState: ControlState, + ) => { + const { controls } = state; + const originalMapStateToProps = + sharedControls?.groupby?.mapStateToProps; + const newState = + originalMapStateToProps?.(state, controlState) ?? {}; + newState.externalValidationErrors = + // @ts-ignore + isAggMode({ controls }) && + ensureIsArray(controlState.value).length === 0 + ? [t('must have a value')] + : []; + return newState; + }, + visibility: isAggMode, + canCopy: true, + }, + }, + ], + [ + { + name: 'columns', + config: { + type: 'SelectControl', + label: t('Dimensions'), + description: t('Columns to display'), + multi: true, + freeForm: true, + allowAll: true, + default: [], + canSelectAll: true, + rerender: ['advanced_data_type_selection'], + optionRenderer: (c: ColumnMeta) => ( + // eslint-disable-next-line react/react-in-jsx-scope + + ), + // eslint-disable-next-line react/react-in-jsx-scope + valueRenderer: (c: ColumnMeta) => ( + // eslint-disable-next-line react/react-in-jsx-scope + + ), + valueKey: 'column_name', + mapStateToProps: ( + state: ControlPanelState, + controlState: ControlState, + ) => { + const { controls } = state; + const originalMapStateToProps = + sharedControls?.columns?.mapStateToProps; + const newState = + originalMapStateToProps?.(state, controlState) ?? {}; + newState.externalValidationErrors = + // @ts-ignore + isRawMode({ controls }) && + ensureIsArray(controlState.value).length === 0 + ? [t('must have a value')] + : []; + return newState; + }, + visibility: isRawMode, + canCopy: true, + }, + }, + ], + [ + { + name: 'advanced_data_type_selection', + config: { + type: 'SelectControl', + label: t('Advanced Data Type'), + description: t( + 'The advanced data type used to filter the rows to display', + ), + multi: false, + rerender: ['advanced_data_type_value'], + default: [], + valueKey: 'value', + mapStateToProps: (state: ControlPanelState) => { + const options = state.datasource?.columns + ? bootstrapData?.common?.advanced_data_types + ?.filter(v => + (state.datasource as Dataset).columns.some( + c => c.advanced_data_type === v.id, + ), + ) + .map(v => ({ value: v.id, label: v.verbose_name })) + : []; + return { options }; + }, + resetOnHide: false, + }, + }, + ], + [ + { + name: 'advanced_data_type_value', + config: { + type: AdvancedDataTypeValue, + freeForm: true, + label: t('Values'), + description: t( + 'The value to be used along with the advanced data type to filter the rows to display', + ), + multi: true, + default: [], + resetOnHide: false, + mapStateToProps: ( + state: ControlPanelState, + controlState: ControlState, + ) => { + const { datasource } = state; + const advancedDataType = + state.controls?.advanced_data_type_selection?.value || ''; + const disabled = ensureIsArray(advancedDataType).length === 0; + const val: any = controlState.value; + const externalValidationErrors = + !disabled && ensureIsArray(val)[0]?.columns.length === 0 + ? [t('Must have a valid entry')] + : []; + return { + datasource, + advancedDataType, + disabled, + externalValidationErrors, + }; + }, + }, + }, + ], + [ + { + name: 'adhoc_filters_no_date_default', + config: { + type: AdhocFilterControl, + label: t('Filters'), + mapStateToProps(state, controlState, chartState) { + const { datasource } = state; + const columns = datasource?.columns; + return { datasource, columns }; + }, + }, + }, + ], + [ + { + name: 'order_by_cols', + config: { + type: 'SelectControl', + label: t('Ordering'), + description: t('Order results by selected columns'), + multi: true, + default: [], + mapStateToProps: ({ datasource }) => ({ + choices: datasource?.hasOwnProperty('order_by_choices') + ? (datasource as Dataset)?.order_by_choices + : datasource?.columns || [], + }), + resetOnHide: false, + }, + }, + ], + [ + { + name: 'row_limit', + override: { + label: t('Row Limit'), + default: 100, + }, + }, + ], + [ + { + name: 'column_state', + config: { + type: 'HiddenControl', + hidden: true, + label: t('Column state'), + description: t('State of AG Grid columns'), + dontRefreshOnChange: true, + }, + }, + ], + ], + }, + ], + // override controls that are inherited by the default configuration + controlOverrides: { + series: { + validators: [validateNonEmpty], + clearable: false, + }, + viz_type: { + default: 'cccs_grid', + }, + time_range: { + default: t('Last day'), + }, + }, +}; + +export default config; diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/index.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/index.ts new file mode 100644 index 0000000000000..0de32b4ec8318 --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/index.ts @@ -0,0 +1,67 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { t, Behavior, ChartMetadata, ChartPlugin } from '@superset-ui/core'; +import buildQuery from './buildQuery'; +import controlPanel from './controlPanel'; +import transformProps from './transformProps'; +import thumbnail from '../images/thumbnail.png'; +import { CccsGridQueryFormData, CccsGridChartProps } from '../types'; +import example1 from '../images/Table1.png'; + +export default class CccsDataSetExplorerPlugin extends ChartPlugin< + CccsGridQueryFormData, + CccsGridChartProps +> { + /** + * The constructor is used to pass relevant metadata and callbacks that get + * registered in respective registries that are used throughout the library + * and application. A more thorough description of each property is given in + * the respective imported file. + * + * It is worth noting that `buildQuery` and is optional, and only needed for + * advanced visualizations that require either post processing operations + * (pivoting, rolling aggregations, sorting etc) or submitting multiple queries. + */ + constructor() { + const metadata = new ChartMetadata({ + description: t('Dataset Explorer: An AG Grid control for Hogwarts data.'), + name: t('Dataset Explorer'), + category: t('Table'), + tags: [ + t('Hogwarts'), + t('Table'), + t('Grid'), + t('Popular'), + t('Report'), + t('Tabular'), + ], + exampleGallery: [{ url: example1 }], + thumbnail, + behaviors: [Behavior.INTERACTIVE_CHART], + }); + + super({ + buildQuery, + controlPanel, + loadChart: () => import('../../../plugin-chart-cccs-grid/src/CccsGrid'), + metadata, + transformProps, + }); + } +} diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/transformProps.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/transformProps.ts new file mode 100644 index 0000000000000..ed796c38339a9 --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/plugin/transformProps.ts @@ -0,0 +1,348 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { ValueFormatterParams } from '@ag-grid-enterprise/all-modules'; +import { + Column, + getMetricLabel, + getNumberFormatter, + Metric, + NumberFormats, + QueryMode, + TimeseriesDataRecord, +} from '@superset-ui/core'; +import { + CccsGridChartProps, + CccsGridQueryFormData, + DEFAULT_FORM_DATA, +} from '../types'; +import {rendererMap, formatIpV4} from '../../../plugin-chart-cccs-grid/src/utils/advancedDataTypes' +export default function transformProps(chartProps: CccsGridChartProps) { + /** + * This function is called after a successful response has been + * received from the chart data endpoint, and is used to transform + * the incoming data prior to being sent to the Visualization. + * + * The transformProps function is also quite useful to return + * additional/modified props to your data viz component. The formData + * can also be accessed from your CccsGrid.tsx file, but + * supplying custom props here is often handy for integrating third + * party libraries that rely on specific props. + * + * A description of properties in `chartProps`: + * - `height`, `width`: the height/width of the DOM element in which + * the chart is located + * - `formData`: the chart data request payload that was sent to the + * backend. + * - `queriesData`: the chart data response payload that was received + * from the backend. Some notable properties of `queriesData`: + * - `data`: an array with data, each row with an object mapping + * the column/alias to its value. Example: + * `[{ col1: 'abc', metric1: 10 }, { col1: 'xyz', metric1: 20 }]` + * - `rowcount`: the number of rows in `data` + * - `query`: the query that was issued. + * + * Please note: the transformProps function gets cached when the + * application loads. When making changes to the `transformProps` + * function during development with hot reloading, changes won't + * be seen until restarting the development server. + */ + const { + datasource, + hooks, + width, + height, + rawFormData: formData, + queriesData, + } = chartProps; + const { + boldText, + headerFontSize, + headerText, + emitFilter, + principalColumns, + query_mode, + column_state, + }: CccsGridQueryFormData = { ...DEFAULT_FORM_DATA, ...formData }; + const data = queriesData[0].data as TimeseriesDataRecord[]; + const agGridLicenseKey = queriesData[0].agGridLicenseKey as String; + + const default_group_by: any[] = []; + const enable_row_numbers: boolean = true; + const jump_action_configs: string[] = [] + const enable_json_expand: boolean = false + + const include_search: boolean = true; + const page_length: number = 50; + const enable_grouping: boolean = true; + + + const { setDataMask = () => {}, setControlValue } = hooks; + const columns = datasource?.columns as Column[]; + const metrics = datasource?.metrics as Metric[]; + + const columnTypeMap = new Map(); + columns.reduce(function (columnMap, column: Column) { + // @ts-ignore + const name = column.column_name; + // @ts-ignore + columnMap[name] = column.type; + return columnMap; + }, columnTypeMap); + + // Map of column advanced types, key is column name, value is column type + const columnAdvancedTypeMap = new Map(); + columns.reduce(function (columnMap, column: Column) { + // @ts-ignore + const name = column.column_name; + // @ts-ignore + columnMap[name] = ( + (column.advanced_data_type as string) ?? '' + ).toUpperCase(); + return columnMap; + }, columnAdvancedTypeMap); + + // Map of verbose names, key is column name, value is verbose name + const columnVerboseNameMap = new Map(); + columns.reduce(function (columnMap, column: Column) { + // @ts-ignore + const name = column.column_name; + // @ts-ignore + columnMap[name] = column.verbose_name; + return columnMap; + }, columnVerboseNameMap); + + // Map of column descriptions, key is column name, value is the description + const columnDescriptionMap = new Map(); + columns.reduce(function (columnMap, column: Column) { + // @ts-ignore + const name = column.column_name; + // @ts-ignore + columnMap[name] = column.description; + return columnMap; + }, columnDescriptionMap); + + // Map of verbose names, key is metric name, value is verbose name + const metricVerboseNameMap = new Map(); + metrics.reduce(function (metricMap, metric: Metric) { + // @ts-ignore + const name = metric.metric_name; + // @ts-ignore + metricMap[name] = metric.verbose_name; + return metricMap; + }, metricVerboseNameMap); + + + const valueFormatter = (params: any) => { + if ( + params.value != null && + params.colDef.cellRenderer === 'ipv4ValueRenderer' + ) { + return formatIpV4(params.value.toString()); + } + return params.value != null ? params.value.toString() : ''; + }; + + const percentMetricValueFormatter = function (params: ValueFormatterParams) { + return getNumberFormatter(NumberFormats.PERCENT_3_POINT).format( + params.value, + ); + }; + + let columnDefs: Column[] = []; + + if (query_mode === QueryMode.raw) { + columnDefs = formData.columns.map((column: any) => { + const columnType = columnTypeMap[column]; + const columnAdvancedType = columnAdvancedTypeMap[column]; + const columnHeader = columnVerboseNameMap[column] + ? columnVerboseNameMap[column] + : column; + const cellRenderer = + columnAdvancedType in rendererMap + ? rendererMap[columnAdvancedType] + : columnType in rendererMap + ? rendererMap[columnType] + : undefined; + const isSortable = true; + const enableRowGroup = true; + const columnDescription = columnDescriptionMap[column]; + const autoHeight = true; + const rowGroupIndex = default_group_by.findIndex( + (element: any) => element === column, + ); + const rowGroup = rowGroupIndex >= 0; + const hide = rowGroup; + return { + field: column, + headerName: columnHeader, + cellRenderer, + sortable: isSortable, + enableRowGroup, + rowGroup, + hide, + rowGroupIndex, + getQuickFilterText: (params: any) => valueFormatter(params), + headerTooltip: columnDescription, + autoHeight, + }; + }); + } else { + if (formData.groupby) { + const groupByColumnDefs = formData.groupby.map((column: any) => { + const columnType = columnTypeMap[column]; + const columnAdvancedType = columnAdvancedTypeMap[column]; + const columnHeader = columnVerboseNameMap[column] + ? columnVerboseNameMap[column] + : column; + const cellRenderer = + columnAdvancedType in rendererMap + ? rendererMap[columnAdvancedType] + : columnType in rendererMap + ? rendererMap[columnType] + : undefined; + const isSortable = true; + const enableRowGroup = true; + const columnDescription = columnDescriptionMap[column]; + const autoHeight = true; + const rowGroupIndex = default_group_by.findIndex( + (element: any) => element === column, + ); + const initialRowGroupIndex = rowGroupIndex; + const rowGroup = rowGroupIndex >= 0; + const hide = rowGroup; + return { + field: column, + headerName: columnHeader, + cellRenderer, + sortable: isSortable, + enableRowGroup, + rowGroup, + rowGroupIndex, + initialRowGroupIndex, + hide, + getQuickFilterText: (params: any) => valueFormatter(params), + headerTooltip: columnDescription, + autoHeight, + }; + }); + columnDefs = columnDefs.concat(groupByColumnDefs); + } + + if (formData.metrics) { + const metricsColumnDefs = formData.metrics + .map(getMetricLabel) + .map((metric: any) => { + const metricHeader = metricVerboseNameMap[metric] + ? metricVerboseNameMap[metric] + : metric; + return { + field: metric, + headerName: metricHeader, + sortable: true, + enableRowGroup: true, + }; + }); + columnDefs = columnDefs.concat(metricsColumnDefs); + } + + if (formData.percent_metrics) { + const percentMetricsColumnDefs = formData.percent_metrics + .map(getMetricLabel) + .map((metric: any) => { + const metricHeader = metricVerboseNameMap[metric] + ? metricVerboseNameMap[metric] + : metric; + return { + field: `%${metric}`, + headerName: `%${metricHeader}`, + sortable: true, + valueFormatter: percentMetricValueFormatter, + }; + }); + columnDefs = columnDefs.concat(percentMetricsColumnDefs); + } + } + + if (enable_row_numbers) { + columnDefs.splice(0, 0, { + headerName: '#', + colId: 'rowNum', + pinned: 'left', + lockVisible: true, + valueGetter: (params: any) => + params.node ? params.node.rowIndex + 1 : null, + } as any); + } + const parsed_jump_action_configs = {}; + jump_action_configs?.forEach((e: any) => { + if (e.dashboardID in parsed_jump_action_configs) { + parsed_jump_action_configs[e.dashboardID] = parsed_jump_action_configs[ + e.dashboardID + ].concat({ + advancedDataType: e.advancedDataType, + nativefilters: e.filters, + name: e.dashBoardName, + }); + } else { + parsed_jump_action_configs[e.dashboardID] = [ + { + advancedDataType: e.advancedDataType, + nativefilters: e.filters, + name: e.dashBoardName, + }, + ]; + } + }); + + // If the flag is set to true, add a column which will contain + // a button to expand all JSON blobs in the row + if (enable_json_expand) { + columnDefs.splice(1, 0, { + colId: 'jsonExpand', + pinned: 'left', + cellRenderer: 'expandAllValueRenderer', + autoHeight: true, + minWidth: 105, + lockVisible: true, + } as any); + } + + return { + formData, + setDataMask, + setControlValue, + width, + height, + columnDefs, + rowData: data, + // and now your control data, manipulated as needed, and passed through as props! + boldText, + headerFontSize, + headerText, + emitFilter, + principalColumns, + include_search, + page_length, + enable_grouping, + column_state, + agGridLicenseKey, + datasetColumns: columns, + jumpActionConfigs: parsed_jump_action_configs, + }; +} diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/types.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/types.ts new file mode 100644 index 0000000000000..acbc1cc6d3e92 --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-dataset-explorer/src/types.ts @@ -0,0 +1,103 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { ColumnState } from '@ag-grid-enterprise/all-modules'; +import { + ChartDataResponseResult, + ChartProps, + HandlerFunction, + QueryFormData, + SetDataMaskHook, + supersetTheme, + TimeseriesDataRecord, + Column, + AdhocFilter, +} from '@superset-ui/core'; + +export type CccsGridQueryFormData = QueryFormData & { + headerText?: string; + setDataMask?: SetDataMaskHook; + selectedValues?: Record; + emitFilter: boolean; + include_search: boolean; + page_length: number; + enable_grouping: boolean; + column_state: ColumnState[]; + enable_row_numbers: boolean; + jump_action_configs?: any[]; + datasource_config?: string; + advanced_data_type_selection: string[]; + advanced_data_type_value: { + columns: string[]; + data: string[]; + rawData: string[]; + }; + adhoc_filters_no_date_default: AdhocFilter[]; +}; + +export interface CccsGridStylesProps { + height: number; + width: number; + headerFontSize?: keyof typeof supersetTheme.typography.sizes; + boldText?: boolean; +} + +// @ts-ignore +export const DEFAULT_FORM_DATA: CccsGridQueryFormData = { + result_type: 'post_processed', + viz_type: 'cccs_grid', +}; + +export interface CccsGridChartDataResponseResult + extends ChartDataResponseResult { + agGridLicenseKey: string; +} + +export class CccsGridChartProps extends ChartProps { + declare formData: CccsGridQueryFormData; + + declare queriesData: CccsGridChartDataResponseResult[]; +} + +export interface CccsGridTransformedProps extends CccsGridStylesProps { + formData: CccsGridQueryFormData; + setDataMask: SetDataMaskHook; + setControlValue: HandlerFunction; + selectedValues: Record; + emitFilter: boolean; + principalColumns: any; + data: TimeseriesDataRecord[]; + columnDefs: any; + rowData: any; + tooltipShowDelay: any; + frameworkComponents: any; + modules: any; + defaultColDef: any; + rowSelection: any; + filters: any; + include_search: boolean; + page_length: number; + enable_grouping: boolean; + column_state: ColumnState[]; + // add typing here for the props you pass in from transformProps.ts! + agGridLicenseKey: string; + datasetColumns: Column[]; + jumpActionConfigs?: any[]; +} + +export type EventHandlers = Record; diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx index e65e25b9b3756..eb7c45fb3cbfd 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx @@ -112,6 +112,7 @@ export interface AdvancedDataTypesState { advancedDataTypeOperatorList: string[]; errorMessage: string; useDefaultOperators: boolean; + values: any[]; } export const useSimpleTabFilterProps = (props: Props) => { diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/useAdvancedDataTypes.ts b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/useAdvancedDataTypes.ts index 31c6af0ba652a..3d8fe972842ab 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/useAdvancedDataTypes.ts +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/useAdvancedDataTypes.ts @@ -27,6 +27,7 @@ const INITIAL_ADVANCED_DATA_TYPES_STATE: AdvancedDataTypesState = { advancedDataTypeOperatorList: [], errorMessage: '', useDefaultOperators: false, + values: [], }; const useAdvancedDataTypes = (validHandler: (isValid: boolean) => void) => { @@ -61,6 +62,7 @@ const useAdvancedDataTypes = (validHandler: (isValid: boolean) => void) => { advancedDataTypeOperatorList: json.result.valid_filter_operators, errorMessage: json.result.error_message, useDefaultOperators: false, + values: json.result.values, }); // Changed due to removal of status field validHandler(!json.result.error_message); @@ -72,6 +74,7 @@ const useAdvancedDataTypes = (validHandler: (isValid: boolean) => void) => { advancedDataTypesState.advancedDataTypeOperatorList, errorMessage: t('Failed to retrieve advanced type'), useDefaultOperators: true, + values: [], }); validHandler(true); }); diff --git a/superset-frontend/src/visualizations/presets/MainPreset.js b/superset-frontend/src/visualizations/presets/MainPreset.js index 11c68fd8f23c5..7026b59c3ab28 100644 --- a/superset-frontend/src/visualizations/presets/MainPreset.js +++ b/superset-frontend/src/visualizations/presets/MainPreset.js @@ -85,6 +85,7 @@ import { GwwkDatasetsChartPlugin, GwwkDashboardsChartPlugin, CccsGridChartPlugin, + DatasetExplorerChartPlugin, BigNumberChartPlugin, AtAGlanceChartIpPlugin, AtAGlanceChartDnsPlugin, @@ -120,6 +121,7 @@ export default class MainPreset extends Preset { new AtAGlanceChartIpPlugin().configure({ key: 'at_a_glance_ip' }), new AtAGlanceChartDnsPlugin().configure({ key: 'at_a_glance_dns' }), new IFrameVisualizationChartPlugin().configure({ key: 'i_frame' }), + new DatasetExplorerChartPlugin().configure({ key: 'dataset_explorer' }), new GwwkChartsChartPlugin().configure({ key: 'gwwk_charts' }), new GwwkDatasetsChartPlugin().configure({ key: 'gwwk_datasets' }), new GwwkDashboardsChartPlugin().configure({ key: 'gwwk_dashboards' }),