From ffa405d738437fd46fb9df6cfbabaee93cec0544 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Mon, 7 Dec 2020 19:24:30 +0100 Subject: [PATCH] feat(ui/explorer/flux): use flux to determine InfluxDB v2 schema --- ui/src/flux/components/DatabaseList.tsx | 34 +++++++-- ui/src/flux/components/FetchMeasurements.tsx | 11 ++- ui/src/flux/components/SchemaExplorer.tsx | 5 +- .../components/TimeMachine/FluxQueryMaker.tsx | 8 ++- .../TimeMachine/FluxScriptWizard.tsx | 69 ++++++++++++------- ui/src/shared/utils/fluxScriptWizard.ts | 2 +- 6 files changed, 92 insertions(+), 37 deletions(-) diff --git a/ui/src/flux/components/DatabaseList.tsx b/ui/src/flux/components/DatabaseList.tsx index 87c4b4b97..f85f9c71e 100644 --- a/ui/src/flux/components/DatabaseList.tsx +++ b/ui/src/flux/components/DatabaseList.tsx @@ -7,10 +7,29 @@ import showDatabasesParser from 'src/shared/parsing/showDatabases' import {ErrorHandling} from 'src/shared/decorators/errors' import {Source, NotificationAction} from 'src/types' +import {executeQuery} from 'src/shared/apis/flux/query' +import {parseResponse} from 'src/shared/parsing/flux/response' interface Props { source: Source notify: NotificationAction + v2?: boolean +} + +export async function getBuckets(source: Source): Promise { + const {csv} = await executeQuery(source, 'buckets()') + const tables = parseResponse(csv) + if (tables && tables.length > 0) { + const data = tables[0].data + if (data.length > 1) { + const nameIndex = data[0].indexOf('name') + if (nameIndex > 0) { + const buckets = data.slice(1).map(arr => arr[nameIndex] as string) + return buckets.sort() + } + } + } + return [] } interface State { @@ -32,13 +51,16 @@ class DatabaseList extends PureComponent { } public async getDatabases() { - const {source} = this.props + const {source, v2} = this.props try { - const {data} = await showDatabases(source.links.proxy) - const {databases} = showDatabasesParser(data) - const sorted = databases.sort() - - this.setState({databases: sorted}) + if (v2) { + const buckets = await getBuckets(source) + this.setState({databases: buckets}) + } else { + const {data} = await showDatabases(source.links.proxy) + const {databases} = showDatabasesParser(data) + this.setState({databases: databases.sort()}) + } } catch (err) { console.error(err) } diff --git a/ui/src/flux/components/FetchMeasurements.tsx b/ui/src/flux/components/FetchMeasurements.tsx index 06df8a172..96059dfe1 100644 --- a/ui/src/flux/components/FetchMeasurements.tsx +++ b/ui/src/flux/components/FetchMeasurements.tsx @@ -19,6 +19,14 @@ interface State { loading: RemoteDataState } +export async function fetchFluxMeasurements( + source: Source, + bucket: string +): Promise { + const measurementResults = await fetchMeasurementsAsync(source, bucket) + return parseValuesColumn(measurementResults) +} + class FetchMeasurements extends PureComponent { constructor(props) { super(props) @@ -39,8 +47,7 @@ class FetchMeasurements extends PureComponent { const {source, bucket} = this.props this.setState({loading: RemoteDataState.Loading}) try { - const measurementResults = await fetchMeasurementsAsync(source, bucket) - const measurements = parseValuesColumn(measurementResults) + const measurements = await fetchFluxMeasurements(source, bucket) this.setState({measurements, loading: RemoteDataState.Done}) } catch (error) { this.setState({loading: RemoteDataState.Error}) diff --git a/ui/src/flux/components/SchemaExplorer.tsx b/ui/src/flux/components/SchemaExplorer.tsx index 944dfd4f2..1a82f7263 100644 --- a/ui/src/flux/components/SchemaExplorer.tsx +++ b/ui/src/flux/components/SchemaExplorer.tsx @@ -7,15 +7,16 @@ import {Source, NotificationAction} from 'src/types' interface Props { source: Source notify: NotificationAction + v2?: boolean } class SchemaExplorer extends PureComponent { public render() { - const {source, notify} = this.props + const {source, notify, v2} = this.props return (
- +
) diff --git a/ui/src/shared/components/TimeMachine/FluxQueryMaker.tsx b/ui/src/shared/components/TimeMachine/FluxQueryMaker.tsx index d4bda383c..b7cda16d0 100644 --- a/ui/src/shared/components/TimeMachine/FluxQueryMaker.tsx +++ b/ui/src/shared/components/TimeMachine/FluxQueryMaker.tsx @@ -12,7 +12,7 @@ import {Button, ComponentSize, ComponentColor} from 'src/reusable_ui' import FluxFunctionsToolbar from 'src/flux/components/flux_functions_toolbar/FluxFunctionsToolbar' // Constants -import {HANDLE_VERTICAL} from 'src/shared/constants' +import {HANDLE_VERTICAL, SOURCE_TYPE_INFLUX_V2} from 'src/shared/constants' // Utils import {getAST} from 'src/shared/apis/flux/ast' @@ -90,6 +90,7 @@ class FluxQueryMaker extends PureComponent { const {suggestions, isWizardActive, draftScriptStatus} = this.state const [leftSize, middleSize, rightSize] = fluxProportions + const v2 = source.type === SOURCE_TYPE_INFLUX_V2 const divisions = [ { @@ -97,7 +98,9 @@ class FluxQueryMaker extends PureComponent { size: leftSize, headerButtons: [], menuOptions: [], - render: () => , + render: () => ( + + ), headerOrientation: HANDLE_VERTICAL, }, { @@ -152,6 +155,7 @@ class FluxQueryMaker extends PureComponent { diff --git a/ui/src/shared/components/TimeMachine/FluxScriptWizard.tsx b/ui/src/shared/components/TimeMachine/FluxScriptWizard.tsx index 70310e5b6..68bf47126 100644 --- a/ui/src/shared/components/TimeMachine/FluxScriptWizard.tsx +++ b/ui/src/shared/components/TimeMachine/FluxScriptWizard.tsx @@ -33,6 +33,10 @@ import { // Types import {RemoteDataState, Source} from 'src/types' +import {getBuckets} from 'src/flux/components/DatabaseList' +import {fetchFluxMeasurements} from 'src/flux/components/FetchMeasurements' +import {fieldsByMeasurement} from 'src/shared/apis/flux/metaQueries' +import {parseFieldsByMeasurements} from 'src/shared/parsing/flux/values' // These constants are selected so that the dropdown menus will not overflow // out of the `.flux-script-wizard--wizard` window @@ -45,6 +49,7 @@ interface Props { isWizardActive: boolean onSetIsWizardActive: (isWizardActive: boolean) => void onAddToScript: (script: string) => void + v2?: boolean } interface State { @@ -86,13 +91,6 @@ class FluxScriptWizard extends PureComponent { public render() { const {children, isWizardActive} = this.props - const { - measurements, - fields, - selectedMeasurement, - selectedFields, - selectedAggFunction, - } = this.state if (!isWizardActive) { return ( @@ -101,6 +99,13 @@ class FluxScriptWizard extends PureComponent { ) } + const { + measurements, + fields, + selectedMeasurement, + selectedFields, + selectedAggFunction, + } = this.state return (
@@ -249,11 +254,9 @@ class FluxScriptWizard extends PureComponent { } private get buttonStatus(): ComponentStatus { - const {selectedDB, selectedRP, selectedMeasurement} = this.state + const {selectedDB, selectedMeasurement} = this.state - const needsSelection = [selectedDB, selectedRP, selectedMeasurement].some( - isEmpty - ) + const needsSelection = [selectedDB, selectedMeasurement].some(isEmpty) const buttonStatus = needsSelection ? ComponentStatus.Disabled @@ -304,7 +307,7 @@ class FluxScriptWizard extends PureComponent { } private fetchAndSetDBsToRPs = async () => { - const {source} = this.props + const {source, v2} = this.props this.setState({ dbsToRPs: {}, @@ -322,7 +325,15 @@ class FluxScriptWizard extends PureComponent { let dbsToRPs try { - dbsToRPs = await this.fetchDBsToRPs(source.links.proxy) + if (v2) { + const buckets = await getBuckets(source) + dbsToRPs = buckets.reduce((acc, db) => { + acc[db] = [''] + return acc + }, {}) + } else { + dbsToRPs = await this.fetchDBsToRPs(source.links.proxy) + } } catch { this.setState({dbsToRPsStatus: RemoteDataState.Error}) @@ -343,7 +354,7 @@ class FluxScriptWizard extends PureComponent { } private fetchAndSetMeasurements = async () => { - const {source} = this.props + const {source, v2} = this.props const {selectedDB} = this.state this.setState({ @@ -358,10 +369,14 @@ class FluxScriptWizard extends PureComponent { let measurements try { - measurements = await this.fetchMeasurements( - source.links.proxy, - selectedDB - ) + if (v2) { + measurements = await fetchFluxMeasurements(source, selectedDB) + } else { + measurements = await this.fetchMeasurements( + source.links.proxy, + selectedDB + ) + } } catch { this.setState({ measurements: [], @@ -383,7 +398,7 @@ class FluxScriptWizard extends PureComponent { } private fetchAndSetFields = async () => { - const {source} = this.props + const {source, v2} = this.props const {selectedDB, selectedMeasurement} = this.state this.setState({ @@ -395,11 +410,17 @@ class FluxScriptWizard extends PureComponent { let fields try { - fields = await this.fetchFields( - source.links.proxy, - selectedDB, - selectedMeasurement - ) + if (v2) { + const fieldsResults = await fieldsByMeasurement(source, selectedDB) + const {fieldsByMeasurements} = parseFieldsByMeasurements(fieldsResults) + fields = fieldsByMeasurements[selectedMeasurement] || [] + } else { + fields = await this.fetchFields( + source.links.proxy, + selectedDB, + selectedMeasurement + ) + } } catch { this.setState({ fields: [], diff --git a/ui/src/shared/utils/fluxScriptWizard.ts b/ui/src/shared/utils/fluxScriptWizard.ts index d8747458d..c441d4cbc 100644 --- a/ui/src/shared/utils/fluxScriptWizard.ts +++ b/ui/src/shared/utils/fluxScriptWizard.ts @@ -62,7 +62,7 @@ export async function fetchFields( } export function formatDBwithRP(db: string, rp: string): string { - return `${db}/${rp}` + return rp ? `${db}/${rp}` : `${db}` } export function toComponentStatus(