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 0000000000000..b3c8eb2228ba7 Binary files /dev/null and b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/images/thumbnail.png differ diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/images/thumbnail.png:Zone.Identifier b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/images/thumbnail.png:Zone.Identifier new file mode 100644 index 0000000000000..33a7247de00d7 --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/images/thumbnail.png:Zone.Identifier @@ -0,0 +1,4 @@ +[ZoneTransfer] +ZoneId=3 +ReferrerUrl=https://github.com/CybercentreCanada/superset/blob/feature/CLDN-750-cccs-1.2/superset-frontend/src/cccs-viz/plugins/plugin-chart-at-a-glance/src/images/thumbnail.png +HostUrl=https://raw.githubusercontent.com/CybercentreCanada/superset/feature/CLDN-750-cccs-1.2/superset-frontend/src/cccs-viz/plugins/plugin-chart-at-a-glance/src/images/thumbnail.png diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/index.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/index.ts new file mode 100644 index 0000000000000..aaa756b08cb8f --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/index.ts @@ -0,0 +1 @@ +export { default as IFrameVisualizationChartPlugin } from './plugin'; diff --git a/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/buildQuery.ts b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/buildQuery.ts new file mode 100644 index 0000000000000..22405e6d1202f --- /dev/null +++ b/superset-frontend/src/cccs-viz/plugins/plugin-chart-iframe/src/plugin/buildQuery.ts @@ -0,0 +1,57 @@ +/** + * 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, QueryFormData } from '@superset-ui/core'; + + /** + * 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 default function buildQuery(formData: QueryFormData) { + /* + We receive an ip as a filter, our job is to find everthing there is to know about that ip + We fire multiple queries to multiple data sets and collect the results here. + */ + + const formDataCopy = { + ...formData, + result_type: 'post_processed', + }; + + return buildQueryContext(formDataCopy, baseQueryObject => { + // 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 92d316d78707e..db25ff233521b 100644 --- a/superset-frontend/src/visualizations/presets/MainPreset.js +++ b/superset-frontend/src/visualizations/presets/MainPreset.js @@ -90,6 +90,7 @@ import { AtAGlanceUserIdChartPlugin, AtAGlanceUserIDSasChartPlugin, ApplicationLinksChartPlugin, + IFrameVisualizationChartPlugin, } from 'src/cccs-viz/plugins/'; import FilterBoxChartPlugin from '../FilterBox/FilterBoxChartPlugin'; import TimeTableChartPlugin from '../TimeTable'; @@ -117,6 +118,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' }),