Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9fc45d9
Add credentials section
alyssajoyner Sep 30, 2025
779f2ca
Add tests for credentials section
alyssajoyner Sep 30, 2025
f8775c6
Add TLS and additional settings section
alyssajoyner Oct 3, 2025
3a83574
Update design and add tests
alyssajoyner Oct 6, 2025
30581c2
Merge branch 'main' into alyssa/config-redesign-part-2
alyssajoyner Oct 6, 2025
b3951a2
Lint/spellcheck
alyssajoyner Oct 6, 2025
c9ebbaa
Fix tests
alyssajoyner Oct 6, 2025
c6d246a
Fix spacing and change protocol description
alyssajoyner Oct 7, 2025
352264f
Add tracking and run prettier
alyssajoyner Oct 7, 2025
cccfdbc
Add optional badge
alyssajoyner Oct 7, 2025
510af44
Add indent to http settings section
alyssajoyner Oct 9, 2025
aa8d732
Merge main
alyssajoyner Oct 9, 2025
9b3bc14
Remove merge header
alyssajoyner Oct 9, 2025
d7b894d
Merge branch 'main' into alyssa/config-redesign-part-2
alyssajoyner Oct 9, 2025
e8323cd
Remove unused file
alyssajoyner Oct 9, 2025
c26e47a
Address comments
alyssajoyner Oct 13, 2025
a83b3a6
Merge branch 'main' into alyssa/config-redesign-part-2
alyssajoyner Oct 13, 2025
5cc0ee4
Fixing nits
alyssajoyner Oct 14, 2025
86918de
Merge branch 'main' into alyssa/config-redesign-part-2
alyssajoyner Oct 14, 2025
4d055fc
Update src/views/config-v2/HttpHeadersConfigV2.tsx
alyssajoyner Oct 20, 2025
1544380
Update src/views/config-v2/HttpHeadersConfigV2.tsx
alyssajoyner Oct 20, 2025
f08a389
Update src/views/config-v2/HttpHeadersConfigV2.tsx
alyssajoyner Oct 20, 2025
ef5ebdb
Update src/views/config-v2/HttpHeadersConfigV2.tsx
alyssajoyner Oct 20, 2025
400b1ea
Merge branch 'main' into alyssa/config-redesign-part-2
alyssajoyner Oct 20, 2025
3985a01
Remove deprecated components
alyssajoyner Oct 20, 2025
c420900
Add helper func for logs and traces config
alyssajoyner Oct 20, 2025
6ee993b
Refactor func
alyssajoyner Oct 20, 2025
283778b
Merge branch 'main' into alyssa/config-redesign-part-2
alyssajoyner Oct 20, 2025
7c47626
Update test
alyssajoyner Oct 20, 2025
65f756d
Update component and labels
alyssajoyner Oct 20, 2025
6290ca1
update labels
alyssajoyner Oct 20, 2025
7019eff
fix broken stuff
alyssajoyner Oct 20, 2025
6c28875
remove yarn
alyssajoyner Oct 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cspell.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@
"timerange",
"timeseries",
"timespan",
"TLSCA",
"toggleTip",
"TLSCA",
"TLSSSL",
"Toggletip",
"totime",
"traceid",
Expand Down
12 changes: 8 additions & 4 deletions src/components/configEditor/AliasTableConfig.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { ChangeEvent, useState } from 'react';
import { ConfigSection } from 'components/experimental/ConfigSection';
import { Input, Field, HorizontalGroup, Button } from '@grafana/ui';
import { Input, Field, Stack, Button } from '@grafana/ui';
import { AliasTableEntry } from 'types/config';
import allLabels from 'labels';
import { styles } from 'styles';
import { selectors as allSelectors } from 'selectors';
import { trackClickhouseConfigV2ColumnAliasTableAdded } from 'views/config-v2/tracking';

interface AliasTablesConfigProps {
aliasTables?: AliasTableEntry[];
Expand Down Expand Up @@ -90,7 +91,10 @@ export const AliasTableConfig = (props: AliasTablesConfigProps) => {
icon="plus-circle"
variant="secondary"
size="sm"
onClick={addEntry}
onClick={() => {
addEntry();
trackClickhouseConfigV2ColumnAliasTableAdded();
}}
className={styles.Common.smallBtn}
>
{labels.addTableLabel}
Expand Down Expand Up @@ -123,7 +127,7 @@ const AliasTableEditor = (props: AliasTableEditorProps) => {

return (
<div data-testid={selectors.aliasEditor}>
<HorizontalGroup>
<Stack>
<Field label={labels.targetDatabaseLabel} aria-label={labels.targetDatabaseLabel}>
<Input
data-testid={selectors.targetDatabaseInput}
Expand Down Expand Up @@ -170,7 +174,7 @@ const AliasTableEditor = (props: AliasTableEditorProps) => {
onClick={onRemove}
/>
)}
</HorizontalGroup>
</Stack>
</div>
);
};
8 changes: 3 additions & 5 deletions src/components/configEditor/HttpHeadersConfig.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { ChangeEvent, useMemo, useState } from 'react';
import { ConfigSection } from 'components/experimental/ConfigSection';
import { Input, Field, HorizontalGroup, Switch, SecretInput, Button } from '@grafana/ui';
import { Input, Field, Stack, Switch, SecretInput, Button } from '@grafana/ui';
import { CHHttpHeader } from 'types/config';
import allLabels from 'labels';
import { styles } from 'styles';
Expand Down Expand Up @@ -113,8 +113,6 @@ const HttpHeaderEditor = (props: HttpHeaderEditorProps) => {
<SecretInput
data-testid={selectors.headerValueInput}
width={65}
label=""
aria-label=""
placeholder={labels.secureHeaderValueLabel}
value={value}
isConfigured={isSecureConfigured}
Expand All @@ -139,7 +137,7 @@ const HttpHeaderEditor = (props: HttpHeaderEditorProps) => {
const headerValueLabel = secure ? labels.secureHeaderValueLabel : labels.insecureHeaderValueLabel;
return (
<div data-testid={selectors.headerEditor}>
<HorizontalGroup>
<Stack>
<Field label={labels.headerNameLabel} aria-label={labels.headerNameLabel}>
<Input
data-testid={selectors.headerNameInput}
Expand Down Expand Up @@ -174,7 +172,7 @@ const HttpHeaderEditor = (props: HttpHeaderEditorProps) => {
onClick={onRemove}
/>
)}
</HorizontalGroup>
</Stack>
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/views/CHConfigEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { QuerySettingsConfig } from 'components/configEditor/QuerySettingsConfig
import { LogsConfig } from 'components/configEditor/LogsConfig';
import { TracesConfig } from 'components/configEditor/TracesConfig';
import { HttpHeadersConfig } from 'components/configEditor/HttpHeadersConfig';
import allLabels from 'labels';
import allLabels from '../labels';
import { onHttpHeadersChange, useConfigDefaults } from './CHConfigEditorHooks';
import { AliasTableConfig } from '../components/configEditor/AliasTableConfig';
import * as trackingV1 from './trackingV1';
Expand Down
290 changes: 290 additions & 0 deletions src/views/config-v2/AdditionalSettingsSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
import { ConfigSubSection } from 'components/experimental/ConfigSection';
import allLabels from './labelsV2';
import React, { ChangeEvent, useState } from 'react';
import { DataSourcePluginOptionsEditorProps, onUpdateDatasourceJsonDataOption } from '@grafana/data';
import { AliasTableEntry, CHConfig, CHCustomSetting, CHLogsConfig, CHSecureConfig, CHTracesConfig } from 'types/config';
import { AliasTableConfig } from 'components/configEditor/AliasTableConfig';
import { DefaultDatabaseTableConfig } from 'components/configEditor/DefaultDatabaseTableConfig';
import { LogsConfig } from 'components/configEditor/LogsConfig';
import { QuerySettingsConfig } from 'components/configEditor/QuerySettingsConfig';
import { TracesConfig } from 'components/configEditor/TracesConfig';
import { config } from '@grafana/runtime';
import { TimeUnit } from 'types/queryBuilder';
import { useConfigDefaults } from 'views/CHConfigEditorHooks';
import { gte as versionGte } from 'semver';
import {
Field,
Divider,
Stack,
Input,
Button,
Switch,
Box,
CollapsableSection,
Text,
Badge,
useStyles2,
} from '@grafana/ui';
import { CONFIG_SECTION_HEADERS, CONTAINER_MIN_WIDTH } from './constants';
import {
trackClickhouseConfigV2CustomSettingClicked,
trackClickhouseConfigV2DefaultDbInput,
trackClickhouseConfigV2DefaultTableInput,
trackClickhouseConfigV2EnableRowLimitToggle,
trackClickhouseConfigV2LogsConfig,
trackClickhouseConfigV2QuerySettings,
trackClickhouseConfigV2TracesConfig,
} from './tracking';
import { css } from '@emotion/css';

export interface Props extends DataSourcePluginOptionsEditorProps<CHConfig, CHSecureConfig> {}

export const AdditionalSettingsSection = (props: Props) => {
const { options, onOptionsChange } = props;
const { jsonData } = options;
const labels = allLabels.components.Config.ConfigEditor;
const styles = useStyles2(getStyles);

useConfigDefaults(options, onOptionsChange);

const [customSettings, setCustomSettings] = useState(jsonData.customSettings || []);

const onLogsConfigChange = (key: keyof CHLogsConfig, value: string | boolean | string[]) => {
onOptionsChange({
...options,
jsonData: {
...options.jsonData,
logs: {
...options.jsonData.logs,
[key]: value,
},
},
});
};

const onUpdateLogsConfig = (key: keyof CHLogsConfig, value: string | boolean | string[]) => {
trackClickhouseConfigV2LogsConfig({ [key]: value });
onLogsConfigChange(key, value);
};

const onTracesConfigChange = (key: keyof CHTracesConfig, value: string | boolean) => {
onOptionsChange({
...options,
jsonData: {
...options.jsonData,
traces: {
...options.jsonData.traces,
durationUnit: options.jsonData.traces?.durationUnit || TimeUnit.Nanoseconds,
[key]: value,
},
},
});
};

const onUpdateTracesConfig = (key: keyof CHTracesConfig, value: string | boolean) => {
trackClickhouseConfigV2TracesConfig({ [key]: value });
onTracesConfigChange(key, value);
};

const onAliasTableConfigChange = (aliasTables: AliasTableEntry[]) => {
onOptionsChange({
...options,
jsonData: {
...options.jsonData,
aliasTables,
},
});
};

const onCustomSettingsChange = (customSettings: CHCustomSetting[]) => {
onOptionsChange({
...options,
jsonData: {
...options.jsonData,
customSettings: customSettings.filter((s) => !!s.setting && !!s.value),
},
});
};

return (
<Box
borderStyle="solid"
borderColor="weak"
padding={2}
marginBottom={4}
id={`${CONFIG_SECTION_HEADERS[3].id}`}
minWidth={CONTAINER_MIN_WIDTH}
>
<CollapsableSection
label={
<>
<Text variant="h3">4. {CONFIG_SECTION_HEADERS[3].label}</Text>
<Badge text="optional" color="blue" className={styles.badge} />
</>
}
isOpen={!!CONFIG_SECTION_HEADERS[3].isOpen}
>
<DefaultDatabaseTableConfig
defaultDatabase={jsonData.defaultDatabase}
defaultTable={jsonData.defaultTable}
onDefaultDatabaseChange={(e) => {
trackClickhouseConfigV2DefaultDbInput();
onUpdateDatasourceJsonDataOption(props, 'defaultDatabase')(e);
}}
onDefaultTableChange={(e) => {
trackClickhouseConfigV2DefaultTableInput();
onUpdateDatasourceJsonDataOption(props, 'defaultTable')(e);
}}
/>
<Divider />
<QuerySettingsConfig
connMaxLifetime={jsonData.connMaxLifetime}
dialTimeout={jsonData.dialTimeout}
maxIdleConns={jsonData.maxIdleConns}
maxOpenConns={jsonData.maxOpenConns}
queryTimeout={jsonData.queryTimeout}
validateSql={jsonData.validateSql}
onDialTimeoutChange={(e) => {
trackClickhouseConfigV2QuerySettings({ dialTimeout: Number(e.currentTarget.value) });
onUpdateDatasourceJsonDataOption(props, 'dialTimeout')(e);
}}
onQueryTimeoutChange={(e) => {
trackClickhouseConfigV2QuerySettings({ queryTimeout: Number(e.currentTarget.value) });
onUpdateDatasourceJsonDataOption(props, 'queryTimeout')(e);
}}
onConnMaxLifetimeChange={(e) => {
trackClickhouseConfigV2QuerySettings({ connMaxLifetime: Number(e.currentTarget.value) });
onUpdateDatasourceJsonDataOption(props, 'connMaxLifetime')(e);
}}
onConnMaxIdleConnsChange={(e) => {
trackClickhouseConfigV2QuerySettings({ maxIdleConns: Number(e.currentTarget.value) });
onUpdateDatasourceJsonDataOption(props, 'maxIdleConns')(e);
}}
onConnMaxOpenConnsChange={(e) => {
trackClickhouseConfigV2QuerySettings({ maxOpenConns: Number(e.currentTarget.value) });
onUpdateDatasourceJsonDataOption(props, 'maxOpenConns')(e);
}}
onValidateSqlChange={(e) => {
trackClickhouseConfigV2QuerySettings({ validateSql: e.currentTarget.checked });
onUpdateDatasourceJsonDataOption(props, 'validateSql')(e);
}}
/>
<Divider />
<LogsConfig
logsConfig={jsonData.logs}
onDefaultDatabaseChange={(db) => onUpdateLogsConfig('defaultDatabase', db)}
onDefaultTableChange={(table) => onUpdateLogsConfig('defaultTable', table)}
onOtelEnabledChange={(v) => onUpdateLogsConfig('otelEnabled', v)}
onOtelVersionChange={(v) => onUpdateLogsConfig('otelVersion', v)}
onTimeColumnChange={(c) => onUpdateLogsConfig('timeColumn', c)}
onLevelColumnChange={(c) => onUpdateLogsConfig('levelColumn', c)}
onMessageColumnChange={(c) => onUpdateLogsConfig('messageColumn', c)}
onSelectContextColumnsChange={(c) => onUpdateLogsConfig('selectContextColumns', c)}
onContextColumnsChange={(c) => onUpdateLogsConfig('contextColumns', c)}
/>

<Divider />
<TracesConfig
tracesConfig={jsonData.traces}
onDefaultDatabaseChange={(db) => onUpdateTracesConfig('defaultDatabase', db)}
onDefaultTableChange={(table) => onUpdateTracesConfig('defaultTable', table)}
onOtelEnabledChange={(v) => onUpdateTracesConfig('otelEnabled', v)}
onOtelVersionChange={(v) => onUpdateTracesConfig('otelVersion', v)}
onTraceIdColumnChange={(c) => onUpdateTracesConfig('traceIdColumn', c)}
onSpanIdColumnChange={(c) => onUpdateTracesConfig('spanIdColumn', c)}
onOperationNameColumnChange={(c) => onUpdateTracesConfig('operationNameColumn', c)}
onParentSpanIdColumnChange={(c) => onUpdateTracesConfig('parentSpanIdColumn', c)}
onServiceNameColumnChange={(c) => onUpdateTracesConfig('serviceNameColumn', c)}
onDurationColumnChange={(c) => onUpdateTracesConfig('durationColumn', c)}
onDurationUnitChange={(c) => onUpdateTracesConfig('durationUnit', c)}
onStartTimeColumnChange={(c) => onUpdateTracesConfig('startTimeColumn', c)}
onTagsColumnChange={(c) => onUpdateTracesConfig('tagsColumn', c)}
onServiceTagsColumnChange={(c) => onUpdateTracesConfig('serviceTagsColumn', c)}
onKindColumnChange={(c) => onUpdateTracesConfig('kindColumn', c)}
onStatusCodeColumnChange={(c) => onUpdateTracesConfig('statusCodeColumn', c)}
onStatusMessageColumnChange={(c) => onUpdateTracesConfig('statusMessageColumn', c)}
onStateColumnChange={(c) => onUpdateTracesConfig('stateColumn', c)}
onInstrumentationLibraryNameColumnChange={(c) => onUpdateTracesConfig('instrumentationLibraryNameColumn', c)}
onInstrumentationLibraryVersionColumnChange={(c) =>
onUpdateTracesConfig('instrumentationLibraryVersionColumn', c)
}
onFlattenNestedChange={(c) => onUpdateTracesConfig('flattenNested', c)}
onEventsColumnPrefixChange={(c) => onUpdateTracesConfig('traceEventsColumnPrefix', c)}
onLinksColumnPrefixChange={(c) => onUpdateTracesConfig('traceLinksColumnPrefix', c)}
/>
<Divider />
<AliasTableConfig aliasTables={jsonData.aliasTables} onAliasTablesChange={onAliasTableConfigChange} />
<Divider />
<Field label={labels.enableRowLimit.label} description={labels.enableRowLimit.tooltip}>
<Switch
value={jsonData.enableRowLimit || false}
data-testid={labels.enableRowLimit.testid}
onChange={(e) => {
trackClickhouseConfigV2EnableRowLimitToggle({ rowLimitEnabled: e.currentTarget.checked });
onUpdateDatasourceJsonDataOption(props, 'enableRowLimit')(e);
}}
/>
</Field>
{config.secureSocksDSProxyEnabled && versionGte(config.buildInfo.version, '10.0.0') && (
<Field label={labels.secureSocksProxy.label} description={labels.secureSocksProxy.tooltip}>
<Switch
value={jsonData.enableSecureSocksProxy || false}
onChange={(e) => onUpdateDatasourceJsonDataOption(props, 'enableSecureSocksProxy')(e)}
/>
</Field>
)}
<ConfigSubSection title="Custom Settings">
{customSettings.map(({ setting, value }, i) => {
return (
<Stack key={i} direction="row">
<Field label={`Setting`} aria-label={`Setting`}>
<Input
value={setting}
placeholder={'Setting'}
onChange={(changeEvent: ChangeEvent<HTMLInputElement>) => {
let newSettings = customSettings.concat();
newSettings[i] = { setting: changeEvent.target.value, value };
setCustomSettings(newSettings);
}}
onBlur={() => onCustomSettingsChange(customSettings)}
></Input>
</Field>
<Field label={'Value'} aria-label={`Value`}>
<Input
value={value}
placeholder={'Value'}
onChange={(changeEvent: ChangeEvent<HTMLInputElement>) => {
let newSettings = customSettings.concat();
newSettings[i] = { setting, value: changeEvent.target.value };
setCustomSettings(newSettings);
}}
onBlur={() => {
onCustomSettingsChange(customSettings);
}}
></Input>
</Field>
</Stack>
);
})}
<Button
variant="secondary"
icon="plus"
type="button"
onClick={() => {
trackClickhouseConfigV2CustomSettingClicked();
setCustomSettings([...customSettings, { setting: '', value: '' }]);
}}
>
Add custom setting
</Button>
</ConfigSubSection>
</CollapsableSection>
</Box>
);
};

const getStyles = () => ({
badge: css({
marginLeft: 'auto',
}),
});
Loading
Loading