Skip to content

Commit

Permalink
Query Editor: Fix field options not loading in (#437)
Browse files Browse the repository at this point in the history
  • Loading branch information
nmarrs authored Oct 4, 2024
1 parent 57b6b6d commit 0582f0e
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 49 deletions.
30 changes: 19 additions & 11 deletions src/QueryEditorForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { mockDatasource, mockQuery } from './__mocks__/datasource';
import '@testing-library/jest-dom';
import { select } from 'react-select-event';
import { selectors } from 'tests/selectors';
import { defaultKey, defaultQuery } from 'types';
import { defaultKey, defaultQuery, QueryEditorFieldType } from 'types';
import * as experimental from '@grafana/experimental';

const ds = mockDatasource;
Expand Down Expand Up @@ -39,7 +39,7 @@ describe('QueryEditor', () => {
it('should request regions and use a new one', async () => {
const onChange = jest.fn();
ds.getResource = jest.fn().mockResolvedValue([ds.defaultRegion, 'foo']);
ds.getRegions = jest.fn(() => ds.getResource('regions'));
ds.getRegions = jest.fn(() => ds.getResource(QueryEditorFieldType.Regions));
render(<QueryEditorForm {...props} onChange={onChange} />);

const selectEl = screen.getByLabelText(selectors.components.ConfigEditor.region.input);
Expand All @@ -48,7 +48,7 @@ describe('QueryEditor', () => {

await select(selectEl, 'foo', { container: document.body });

expect(ds.getResource).toHaveBeenCalledWith('regions');
expect(ds.getResource).toHaveBeenCalledWith(QueryEditorFieldType.Regions);
expect(onChange).toHaveBeenCalledWith({
...q,
connectionArgs: { ...q.connectionArgs, region: 'foo' },
Expand All @@ -58,7 +58,9 @@ describe('QueryEditor', () => {
it('should request catalogs and use a new one', async () => {
const onChange = jest.fn();
ds.postResource = jest.fn().mockResolvedValue([ds.defaultCatalog, 'foo']);
ds.getCatalogs = jest.fn((query) => ds.postResource('catalogs', { region: query.connectionArgs.region }));
ds.getCatalogs = jest.fn((query) =>
ds.postResource(QueryEditorFieldType.Catalogs, { region: query.connectionArgs.region })
);
render(<QueryEditorForm {...props} onChange={onChange} />);

const selectEl = screen.getByLabelText(selectors.components.ConfigEditor.catalog.input);
Expand All @@ -67,7 +69,7 @@ describe('QueryEditor', () => {

await select(selectEl, 'foo', { container: document.body });

expect(ds.postResource).toHaveBeenCalledWith('catalogs', { region: defaultKey });
expect(ds.postResource).toHaveBeenCalledWith(QueryEditorFieldType.Catalogs, { region: defaultKey });
expect(onChange).toHaveBeenCalledWith({
...q,
connectionArgs: { ...q.connectionArgs, catalog: 'foo' },
Expand All @@ -79,7 +81,10 @@ describe('QueryEditor', () => {
const onRunQuery = jest.fn();
ds.postResource = jest.fn().mockResolvedValue([ds.defaultDatabase, 'foo']);
ds.getDatabases = jest.fn((query) =>
ds.postResource('databases', { region: query.connectionArgs.region, catalog: query.connectionArgs.catalog })
ds.postResource(QueryEditorFieldType.Databases, {
region: query.connectionArgs.region,
catalog: query.connectionArgs.catalog,
})
);
render(<QueryEditorForm {...props} onChange={onChange} onRunQuery={onRunQuery} />);

Expand All @@ -89,7 +94,10 @@ describe('QueryEditor', () => {

await select(selectEl, 'foo', { container: document.body });

expect(ds.postResource).toHaveBeenCalledWith('databases', { region: defaultKey, catalog: defaultKey });
expect(ds.postResource).toHaveBeenCalledWith(QueryEditorFieldType.Databases, {
region: defaultKey,
catalog: defaultKey,
});
expect(onChange).toHaveBeenCalledWith({
...q,
connectionArgs: { ...q.connectionArgs, database: 'foo' },
Expand All @@ -102,7 +110,7 @@ describe('QueryEditor', () => {
const onRunQuery = jest.fn();
ds.postResource = jest.fn().mockResolvedValue(['foo']);
ds.getTables = jest.fn((query) =>
ds.postResource('tables', {
ds.postResource(QueryEditorFieldType.Tables, {
region: query.connectionArgs.region,
catalog: query.connectionArgs.catalog,
database: query.connectionArgs.database,
Expand All @@ -116,7 +124,7 @@ describe('QueryEditor', () => {
await select(selectEl, 'foo', { container: document.body });

expect(ds.postResource).toHaveBeenCalledWith(
'tables',
QueryEditorFieldType.Tables,
expect.objectContaining({ region: defaultKey, catalog: defaultKey, database: defaultKey })
);
expect(onChange).toHaveBeenCalledWith({
Expand All @@ -131,7 +139,7 @@ describe('QueryEditor', () => {
const onRunQuery = jest.fn();
ds.postResource = jest.fn().mockResolvedValue(['columnName']);
ds.getColumns = jest.fn((query) =>
ds.postResource('columns', {
ds.postResource(QueryEditorFieldType.Columns, {
region: query.connectionArgs.region,
catalog: query.connectionArgs.catalog,
database: query.connectionArgs.database,
Expand All @@ -153,7 +161,7 @@ describe('QueryEditor', () => {
await select(selectEl, 'columnName', { container: document.body });

expect(ds.postResource).toHaveBeenCalledWith(
'columns',
QueryEditorFieldType.Columns,
expect.objectContaining({ region: defaultKey, catalog: defaultKey, database: defaultKey, table: 'tableName' })
);
expect(onChange).toHaveBeenCalledWith({
Expand Down
103 changes: 65 additions & 38 deletions src/QueryEditorForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import React, { useEffect, useState } from 'react';
import { css } from '@emotion/css';
import { GrafanaTheme2, QueryEditorProps, SelectableValue } from '@grafana/data';
import { DataSource } from './datasource';
import { AthenaDataSourceOptions, AthenaQuery, defaultQuery, SelectableFormatOptions } from './types';
import {
AthenaDataSourceOptions,
AthenaQuery,
defaultQuery,
QueryEditorFieldType,
SelectableFormatOptions,
} from './types';
import { CollapsableSection, useStyles2 } from '@grafana/ui';
import { FormatSelect, ResourceSelector } from '@grafana/aws-sdk';
import { selectors } from 'tests/selectors';
Expand All @@ -15,8 +21,6 @@ type Props = QueryEditorProps<DataSource, AthenaQuery, AthenaDataSourceOptions>
hideOptions?: boolean;
};

type QueryProperties = 'regions' | 'catalogs' | 'databases' | 'tables' | 'columns';

export function QueryEditorForm(props: Props) {
const [resultReuseSupported, setResultReuseSupported] = useState(false);
const styles = useStyles2(getStyles);
Expand All @@ -36,6 +40,17 @@ export function QueryEditorForm(props: Props) {
},
};

/*
State used force the component to re-render when fetching
editor field options so they show up in the select component
*/
const [needsUpdate, setNeedsUpdate] = useState(false);
useEffect(() => {
if (needsUpdate) {
setNeedsUpdate(false);
}
}, [needsUpdate]);

// Populate the props.query with defaults on mount
useEffect(() => {
props.onChange(queryWithDefaults);
Expand All @@ -45,40 +60,49 @@ export function QueryEditorForm(props: Props) {

const templateVariables = props.datasource.getVariables();

const fetchRegions = () =>
props.datasource.getRegions().then((regions) => appendTemplateVariables(templateVariables, regions));
const fetchCatalogs = () =>
props.datasource
const fetchRegions = async () =>
await props.datasource
.getRegions()
.then((regions) => appendTemplateVariables(templateVariables, regions))
.finally(() => setNeedsUpdate(true));
const fetchCatalogs = async () =>
await props.datasource
.getCatalogs(queryWithDefaults)
.then((catalogs) => appendTemplateVariables(templateVariables, catalogs));
const fetchDatabases = () =>
props.datasource
.then((catalogs) => appendTemplateVariables(templateVariables, catalogs))
.finally(() => setNeedsUpdate(true));
const fetchDatabases = async () =>
await props.datasource
.getDatabases(queryWithDefaults)
.then((databases) => appendTemplateVariables(templateVariables, databases));
const fetchTables = () =>
props.datasource.getTables(queryWithDefaults).then((tables) => appendTemplateVariables(templateVariables, tables));
const fetchColumns = () =>
props.datasource
.then((databases) => appendTemplateVariables(templateVariables, databases))
.finally(() => setNeedsUpdate(true));
const fetchTables = async () =>
await props.datasource
.getTables(queryWithDefaults)
.then((tables) => appendTemplateVariables(templateVariables, tables))
.finally(() => setNeedsUpdate(true));
const fetchColumns = async () =>
await props.datasource
.getColumns(queryWithDefaults)
.then((columns) => appendTemplateVariables(templateVariables, columns));
.then((columns) => appendTemplateVariables(templateVariables, columns))
.finally(() => setNeedsUpdate(true));

const onChange = (prop: QueryProperties) => (e: SelectableValue<string> | null) => {
const onChange = (prop: QueryEditorFieldType) => (e: SelectableValue<string> | null) => {
const newQuery = { ...props.query };
const value = e?.value;
switch (prop) {
case 'regions':
case QueryEditorFieldType.Regions:
newQuery.connectionArgs = { ...newQuery.connectionArgs, region: value };
break;
case 'catalogs':
case QueryEditorFieldType.Catalogs:
newQuery.connectionArgs = { ...newQuery.connectionArgs, catalog: value };
break;
case 'databases':
case QueryEditorFieldType.Databases:
newQuery.connectionArgs = { ...newQuery.connectionArgs, database: value };
break;
case 'tables':
case QueryEditorFieldType.Tables:
newQuery.table = value;
break;
case 'columns':
case QueryEditorFieldType.Columns:
newQuery.column = value;
break;
}
Expand All @@ -93,11 +117,11 @@ export function QueryEditorForm(props: Props) {
width={15}
label={selectors.components.ConfigEditor.region.input}
data-testid={selectors.components.ConfigEditor.region.wrapper}
htmlFor="regions"
htmlFor={QueryEditorFieldType.Regions}
>
<ResourceSelector
id="regions"
onChange={onChange('regions')}
id={QueryEditorFieldType.Regions}
onChange={onChange(QueryEditorFieldType.Regions)}
fetch={fetchRegions}
value={queryWithDefaults.connectionArgs.region ?? null}
default={props.datasource.defaultRegion}
Expand All @@ -108,11 +132,11 @@ export function QueryEditorForm(props: Props) {
width={15}
label={selectors.components.ConfigEditor.catalog.input}
data-testid={selectors.components.ConfigEditor.catalog.wrapper}
htmlFor="catalogs"
htmlFor={QueryEditorFieldType.Catalogs}
>
<ResourceSelector
id="catalogs"
onChange={onChange('catalogs')}
id={QueryEditorFieldType.Catalogs}
onChange={onChange(QueryEditorFieldType.Catalogs)}
fetch={fetchCatalogs}
value={queryWithDefaults.connectionArgs.catalog ?? null}
default={props.datasource.defaultCatalog}
Expand All @@ -127,11 +151,11 @@ export function QueryEditorForm(props: Props) {
width={20}
label={selectors.components.ConfigEditor.database.input}
data-testid={selectors.components.ConfigEditor.database.wrapper}
htmlFor="databases"
htmlFor={QueryEditorFieldType.Databases}
>
<ResourceSelector
id="databases"
onChange={onChange('databases')}
id={QueryEditorFieldType.Databases}
onChange={onChange(QueryEditorFieldType.Databases)}
fetch={fetchDatabases}
value={queryWithDefaults.connectionArgs.database ?? null}
default={props.datasource.defaultDatabase}
Expand All @@ -144,11 +168,11 @@ export function QueryEditorForm(props: Props) {
label={selectors.components.ConfigEditor.table.input}
data-testid={selectors.components.ConfigEditor.table.wrapper}
tooltip="Use the selected table with the $__table macro"
htmlFor="tables"
htmlFor={QueryEditorFieldType.Tables}
>
<ResourceSelector
id="tables"
onChange={onChange('tables')}
id={QueryEditorFieldType.Tables}
onChange={onChange(QueryEditorFieldType.Tables)}
fetch={fetchTables}
value={props.query.table || null}
dependencies={[queryWithDefaults.connectionArgs.database]}
Expand All @@ -160,12 +184,12 @@ export function QueryEditorForm(props: Props) {
label={selectors.components.ConfigEditor.column.input}
data-testid={selectors.components.ConfigEditor.column.wrapper}
tooltip="Use the selected column with the $__column macro"
htmlFor="columns"
htmlFor={QueryEditorFieldType.Columns}
>
<ResourceSelector
id="columns"
id={QueryEditorFieldType.Columns}
label={selectors.components.ConfigEditor.column.input}
onChange={onChange('columns')}
onChange={onChange(QueryEditorFieldType.Columns)}
fetch={fetchColumns}
value={props.query.column || null}
dependencies={[queryWithDefaults.table]}
Expand Down Expand Up @@ -203,7 +227,7 @@ export function QueryEditorForm(props: Props) {
</div>
</EditorRow>
<EditorRow>
<div style={{ width: '100%' }}>
<div className={styles.sqlEditor}>
<SQLEditor query={props.query} onChange={props.onChange} datasource={props.datasource} />
</div>
</EditorRow>
Expand Down Expand Up @@ -236,4 +260,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
padding: 'unset',
},
}),
sqlEditor: css({
width: '100%',
}),
});
8 changes: 8 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ export enum FormatOptions {
Logs,
}

export enum QueryEditorFieldType {
Regions = 'regions',
Catalogs = 'catalogs',
Databases = 'databases',
Tables = 'tables',
Columns = 'columns',
}

export const SelectableFormatOptions: Array<SelectableValue<FormatOptions>> = [
{
label: 'Time Series',
Expand Down

0 comments on commit 0582f0e

Please sign in to comment.