From a35e6c22e997220ea7b26bf690b579d33b4c1f93 Mon Sep 17 00:00:00 2001 From: cccs-RyanS <71385290+cccs-RyanS@users.noreply.github.com> Date: Thu, 1 Dec 2022 08:57:43 -0500 Subject: [PATCH] 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> --- cccs-build/superset/Dockerfile | 1 + .../src/cccs-viz/plugins/index.ts | 1 + .../src/IFrameVisualization.tsx | 17 ++ .../src/images/thumbnail.png | Bin 0 -> 1547 bytes .../src/images/thumbnail.png:Zone.Identifier | 4 + .../plugins/plugin-chart-iframe/src/index.ts | 1 + .../src/plugin/buildQuery.ts | 57 +++++ .../src/plugin/controlPanel.ts | 205 ++++++++++++++++++ .../plugin-chart-iframe/src/plugin/index.ts | 51 +++++ .../src/plugin/transformProps.ts | 80 +++++++ .../plugins/plugin-chart-iframe/src/styles.js | 22 ++ .../plugins/plugin-chart-iframe/src/types.ts | 9 + .../src/visualizations/presets/MainPreset.js | 2 + 13 files changed, 450 insertions(+) create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/IFrameVisualization.tsx create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/images/thumbnail.png create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/images/thumbnail.png:Zone.Identifier create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/index.ts create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/buildQuery.ts create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/controlPanel.ts create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/index.ts create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/transformProps.ts create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/styles.js create mode 100644 superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/types.ts diff --git a/cccs-build/superset/Dockerfile b/cccs-build/superset/Dockerfile index 0e1e13f04268b..6abb7e38ea3dd 100644 --- a/cccs-build/superset/Dockerfile +++ b/cccs-build/superset/Dockerfile @@ -4,6 +4,7 @@ FROM $VAULT_CA_CONTAINER AS vault_ca FROM uchimera.azurecr.io/cccs/superset-base:cccs-2.0_20221014182839_b5135 + USER root COPY *requirements.txt /tmp/ diff --git a/superset-frontend/src/cccs-viz/plugins/index.ts b/superset-frontend/src/cccs-viz/plugins/index.ts index 7851bb1560dc0..f8453be3c882f 100644 --- a/superset-frontend/src/cccs-viz/plugins/index.ts +++ b/superset-frontend/src/cccs-viz/plugins/index.ts @@ -28,3 +28,4 @@ export { default as AtAGlanceChartDnsPlugin } from './plugin-chart-at-a-glance-d export { default as AtAGlanceUserIdChartPlugin } from './plugin-chart-at-a-glance-user-id/src/plugin'; 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'; diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/IFrameVisualization.tsx b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/IFrameVisualization.tsx new file mode 100644 index 0000000000000..fa1cec2d74420 --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/IFrameVisualization.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { IFrameVisualizationProps } from './types'; + + +export default function IFrameVisualization(props: IFrameVisualizationProps) { + const { url, url_parameter_value, parameter_name, parameter_prefix, errorMessage} = props + + const parserdUrlParameterName = parameter_name.includes('=') ? parameter_name : `${parameter_name}=${parameter_prefix}` + + return ( + <> + { errorMessage ? + <>{errorMessage} : + } + + ); +} diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/images/thumbnail.png b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/images/thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..b3c8eb2228ba700df9fdf3ddeab66af86d572d0f GIT binary patch literal 1547 zcmZvcc~BAv6vjzSQo$=MMZ>EM%q_2gOfymw4-7m;H-!>4TNCpbk94dS98zRe60OXG zP&XFS%rztN#;{8<^CViuEN`W?LmfAko!S0t=6mmZ^UeGB`{m&SyusQ~Z2$lO_Q84v zs$Blx>`+&2Es7OZW$}3bU@xUosX8SRNpf=Xr%#_sOG{-kSy@?GMMXtpW8=ogMn*

93pM>{$?P$-n6 zqhn!Vp+F!2fk1wKeh>&GCnsm_NZcdG`;Cnh+fe7WL6_#1oB$zom2nq~N&8-@pC zmXyjq*1oq`mXTlSjG%B}_WImZXc9IqD5T&W`n$@J{VhcH2_YpnEjF}{NZchH89_={ zlh#UwB-o2SbobJG-{XLYh#M?ro zWzTCdab+k+aCDiOmXmy1O7XwBJ1mhUJc!xFkw22fXf>&US5t26d+N4#{6m8;WbVT< zBAvi>-A2FvIN$KSnZ0c0pemK{f?C<5pHV0~7|PE4N213P_a%Pff8>-wP0X;=`5`%%VBCJOi-g|5PrYjwF z)VIHX#YP_SGiWUJAG7=8uEVKRvaYCe5qeo{i*y$jp7~Q!+O23+Zd^k@AvHFP$zD&G zHs}E}@(A&jdgim5BQnLN(!lEEM|-MP^?3^Vbhz_Ge%1EJ?dRhKF!LxYp3+(>(&pF8 zH`gtA(+@08y&6_*E)t431*&`pzt++sHzL6TQ^36=zmfYzgSvT`>o&uYnozCBw-u1n z>oh$ooL_=i`*>Ha%wECB_<9h{dc9k3YHhqVNeYLWXl5!7Db$kvS;P5A`mjNHU=5R- zXW0UB-J5&BF!NPU5$qK#zL>Gjff|#ka)TxPd9Q2_lILBcC>lQ@ml!=rLE9KvA3{m# zG8mJ<)ZsT-#+K!VGNE2X5&OSn-YZ1Z40FATgBn!xetBADBd+<(`20lUquDA1|;SRXo16<6A1N)ZEw9x|}k0`rIe2^KiWp%u+DKa&$+|0%9no)9r&eU2@DAYd*VI|nB&5-PpGNLgXCQ3Dg zr5i8I^G>_kVNggGOGsoR**;PqEe^wAZvfCTZUUO!Mvn+|Z5>6YcM>TcP1i z+t-%5QZQM4Ei>ND&mZOw9a(<+(iSuLb<^elN+pT~@~fr;#wfGIANQ|I@kuFqw|L2< zO>9D5M7~hZtOIUYX=`TYd}9mYgi3%2sRBXspwaxX^bo3yHWsmzb*^?>=Y;w<@)#d zt1*+(Hrk^L-dh927&X>yfLp>}DWT7!HjMYLcTZe?_=0$l^)~KQ_PycwH(rS5H-Rw3 gk?&YU7l_{L?Vd~@K3as!Rv{hW;}zi9aN;cWUl!ot { + // RAW mode (not aggregated) + // eslint-disable-next-line no-param-reassign + return [ + { + ...baseQueryObject, + row_limit: 10, + }, + ]; + }); + } + \ No newline at end of file diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/controlPanel.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/controlPanel.ts new file mode 100644 index 0000000000000..5c7a9df66fbce --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/controlPanel.ts @@ -0,0 +1,205 @@ +/** + * 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 { ensureIsArray, t, validateNonEmpty } from '@superset-ui/core'; +import { + ControlPanelConfig, + ControlPanelState, + ControlState, + sharedControls, +} from '@superset-ui/chart-controls'; + +const config: ControlPanelConfig = { + + /** + * The control panel is split into two tabs: "Query" and + * "Chart Options". The controls that define the inputs to + * the chart data request, such as columns and metrics, usually + * reside within "Query", while controls that affect the visual + * appearance or functionality of the chart are under the + * "Chart Options" section. + * + * There are several predefined controls that can be used. + * Some examples: + * - groupby: columns to group by (tranlated to GROUP BY statement) + * - series: same as groupby, but single selection. + * - metrics: multiple metrics (translated to aggregate expression) + * - metric: sane as metrics, but single selection + * - adhoc_filters: filters (translated to WHERE or HAVING + * depending on filter type) + * - row_limit: maximum number of rows (translated to LIMIT statement) + * + * If a control panel has both a `series` and `groupby` control, and + * the user has chosen `col1` as the value for the `series` control, + * and `col2` and `col3` as values for the `groupby` control, + * the resulting query will contain three `groupby` columns. This is because + * we considered `series` control a `groupby` query field and its value + * will automatically append the `groupby` field when the query is generated. + * + * It is also possible to define custom controls by importing the + * necessary dependencies and overriding the default parameters, which + * can then be placed in the `controlSetRows` section + * of the `Query` section instead of a predefined control. + * + * import { validateNonEmpty } from '@superset-ui/core'; + * import { + * sharedControls, + * ControlConfig, + * ControlPanelConfig, + * } from '@superset-ui/chart-controls'; + * + * const myControl: ControlConfig<'SelectControl'> = { + * name: 'secondary_entity', + * config: { + * ...sharedControls.entity, + * type: 'SelectControl', + * label: t('Secondary Entity'), + * mapStateToProps: state => ({ + * sharedControls.columnChoices(state.datasource) + * .columns.filter(c => c.groupby) + * }) + * validators: [validateNonEmpty], + * }, + * } + * + * In addition to the basic drop down control, there are several predefined + * control types (can be set via the `type` property) that can be used. Some + * commonly used examples: + * - SelectControl: Dropdown to select single or multiple values, + usually columns + * - MetricsControl: Dropdown to select metrics, triggering a modal + to define Metric details + * - AdhocFilterControl: Control to choose filters + * - CheckboxControl: A checkbox for choosing true/false values + * - SliderControl: A slider with min/max values + * - TextControl: Control for text data + * + * For more control input types, check out the `incubator-superset` repo + * and open this file: superset-frontend/src/explore/components/controls/index.js + * + * To ensure all controls have been filled out correctly, the following + * validators are provided + * by the `@superset-ui/core/lib/validator`: + * - validateNonEmpty: must have at least one value + * - validateInteger: must be an integer value + * - validateNumber: must be an intger or decimal value + */ + + // For control input types, see: superset-frontend/src/explore/components/controls/index.js + controlPanelSections: [ + { + label: t('Query'), + expanded: true, + controlSetRows: [ + ['adhoc_filters'], + [ + { + name: 'url', + config: { + type: 'TextControl', + label: t('URL'), + mapStateToProps: ( + state: ControlPanelState, + controlState: ControlState, + ) => { + const originalMapStateToProps = + sharedControls?.groupby?.mapStateToProps; + const newState = + originalMapStateToProps?.(state, controlState) ?? {}; + newState.externalValidationErrors = controlState.value ? [] : ["Please add a value for URL."] + return newState; + }, + renderTrigger: true, + default: '', + description: t('The Base URL for the Iframe.'), + }, + }, + ], + [ + { + name: 'groupby', + override: { + label: t('Parameter Column Name'), + description: "The name of the column that will populate the url parameter value.", + multi: false, + allowAll: false, + default: [], + includeTime: false, + mapStateToProps: ( + state: ControlPanelState, + controlState: ControlState, + ) => { + const originalMapStateToProps = + sharedControls?.groupby?.mapStateToProps; + const newState = + originalMapStateToProps?.(state, controlState) ?? {}; + newState.externalValidationErrors = ensureIsArray(controlState.value).length > 0 ? [] : ["Please add a value for Parameter Column Name."] + return newState; + }, + }, + }, + ], + [ + { + name: 'parameter_name', + config: { + type: 'TextControl', + label: t('Parameter Name'), + mapStateToProps: ( + state: ControlPanelState, + controlState: ControlState, + ) => { + const originalMapStateToProps = + sharedControls?.groupby?.mapStateToProps; + const newState = + originalMapStateToProps?.(state, controlState) ?? {}; + newState.externalValidationErrors = controlState.value ? [] : ["Please add a value for Parameter Name."] + return newState; + }, + default: '', + description: t('The name for the URL parameter.'), + }, + }, + ], + [ + { + name: 'parameter_prefix', + config: { + type: 'TextControl', + label: t('Parameter Prefix'), + default: '', + description: t('A value that will be prefix the parameter value.'), + }, + }, + ] + ], + }, + ], + + controlOverrides: { + series: { + validators: [validateNonEmpty], + clearable: false, + }, + row_limit: { + default: 1, + }, + }, +}; + +export default config; diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/index.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/index.ts new file mode 100644 index 0000000000000..0efe549552fcb --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/index.ts @@ -0,0 +1,51 @@ +/** + * 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, ChartMetadata, ChartPlugin } from '@superset-ui/core'; +import controlPanel from './controlPanel'; +import transformProps from './transformProps'; +import buildQuery from './buildQuery'; +import thumbnail from '../images/thumbnail.png'; + +export default class IFrameVisualizationChartPlugin extends ChartPlugin { + /** + * 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: 'IFrame Visualization', + name: t('IFrame Visualization'), + thumbnail, + }); + + super({ + buildQuery, + controlPanel, + loadChart: () => import('../IFrameVisualization'), + metadata, + transformProps, + }); + } +} diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/transformProps.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/transformProps.ts new file mode 100644 index 0000000000000..ee67ef9d2aff7 --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/transformProps.ts @@ -0,0 +1,80 @@ +/** + * 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 { ChartProps, TimeseriesDataRecord, } from '@superset-ui/core'; + +export default function transformProps(chartProps: ChartProps) { + /** + * 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 IframeDemo.tsx file, but + * doing 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 formData = chartProps.formData; + const queriesData = chartProps.queriesData; + + const { url, parameterName, parameterPrefix, groupby } = formData + + const data = queriesData[0]?.data as TimeseriesDataRecord[]; + + let value: string | number | true | Date = "" + let errorMessage = ""; + + if(Array.isArray(data) && data.length > 1) { + errorMessage = "The query returned too many rows when only one was expected." + } + + if(Array.isArray(data) && data.length === 0) { + errorMessage = "The query returned no rows." + } + + if(Array.isArray(data) && data.length === 1) { + value = data[0][groupby] || "" + } + + return { + url_parameter_value: value, + parameter_name: parameterName, + url, + parameter_prefix: parameterPrefix, + errorMessage, + }; +} diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/styles.js b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/styles.js new file mode 100644 index 0000000000000..d7bb66e9bb356 --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/styles.js @@ -0,0 +1,22 @@ +const InlineBlock = { + display: 'inline-block', +}; + +const InlineImg = { + display: 'inline-block', + transform: 'translateY(-10%)', + '-ms-transform': 'translateY(-10%)', +}; + +const InlineText = { + display: 'inline-block', + 'text-align': 'center', +}; + +const styles = { + InlineBlock, + InlineImg, + InlineText, +}; + +export default styles; diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/types.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/types.ts new file mode 100644 index 0000000000000..bd24375ee377c --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/types.ts @@ -0,0 +1,9 @@ +import { QueryFormData } from '@superset-ui/core'; + +export type IFrameVisualizationProps = QueryFormData & { + url_parameter_value: string; + parameter_name: string; + url: string; + parameter_prefix: string; + errorMessage: string; +}; diff --git a/superset-frontend/src/visualizations/presets/MainPreset.js b/superset-frontend/src/visualizations/presets/MainPreset.js index 709c32aeff8ca..0c64a7c57ff2e 100644 --- a/superset-frontend/src/visualizations/presets/MainPreset.js +++ b/superset-frontend/src/visualizations/presets/MainPreset.js @@ -91,6 +91,7 @@ import { AtAGlanceUserIdChartPlugin, AtAGlanceUserIDSasChartPlugin, ApplicationLinksChartPlugin, + IFrameVisualizationChartPlugin, } from 'src/cccs-viz/plugins/'; import FilterBoxChartPlugin from '../FilterBox/FilterBoxChartPlugin'; import TimeTableChartPlugin from '../TimeTable'; @@ -118,6 +119,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 GwwkChartsChartPlugin().configure({ key: 'gwwk_charts' }), new GwwkDatasetsChartPlugin().configure({ key: 'gwwk_datasets' }), new GwwkDashboardsChartPlugin().configure({ key: 'gwwk_dashboards' }),