Build plugin with grafana toolkit (#1539)
* Use grafana toolkit template for building plugin * Fix linter and type errors * Update styles building * Fix sass deprecation warning * Remove empty js files produced by webpack building sass * Fix signing script * Replace classnames with cx * Fix data source config page * Use custom webpack config instead of overriding original one * Use gpx_ prefix for plugin executable * Remove unused configs * Roll back react hooks dependencies usage * Move plugin-specific ts config to root config file * Temporary do not use rst2html for function description tooltip * Remove unused code * remove unused dependencies * update react table dependency * Migrate tests to typescript * remove unused dependencies * Remove old webpack configs * Add sign target to makefile * Add magefile * Update CI test job * Update go packages * Update build instructions * Downgrade go version to 1.18 * Fix go version in ci * Fix metric picker * Add comment to webpack config * remove angular mocks * update bra config * Rename datasource-zabbix to datasource (fix mage build) * Add instructions for building backend with mage * Fix webpack targets * Fix ci backend tests * Add initial e2e tests * Fix e2e ci tests * Update docker compose for cypress tests * build grafana docker image * Fix docker stop task * CI: add Grafana compatibility check
This commit is contained in:
345
src/datasource/components/ConfigEditor.tsx
Normal file
345
src/datasource/components/ConfigEditor.tsx
Normal file
@@ -0,0 +1,345 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import { DataSourcePluginOptionsEditorProps, DataSourceSettings, SelectableValue } from '@grafana/data';
|
||||
import { Button, DataSourceHttpSettings, InlineFormLabel, LegacyForms, Select } from '@grafana/ui';
|
||||
import { ZabbixDSOptions, ZabbixSecureJSONData } from '../types';
|
||||
|
||||
const { FormField, Switch } = LegacyForms;
|
||||
|
||||
const SUPPORTED_SQL_DS = ['mysql', 'postgres', 'influxdb'];
|
||||
|
||||
export type Props = DataSourcePluginOptionsEditorProps<ZabbixDSOptions, ZabbixSecureJSONData>;
|
||||
export const ConfigEditor = (props: Props) => {
|
||||
const { options, onOptionsChange } = props;
|
||||
|
||||
const [selectedDBDatasource, setSelectedDBDatasource] = useState(null);
|
||||
const [currentDSType, setCurrentDSType] = useState('');
|
||||
|
||||
// Apply some defaults on initial render
|
||||
useEffect(() => {
|
||||
const { jsonData, secureJsonFields } = options;
|
||||
|
||||
// Set secureJsonFields.password to password and then remove it from config
|
||||
const { password, ...restJsonData } = jsonData;
|
||||
if (!secureJsonFields?.password) {
|
||||
if (!options.secureJsonData) {
|
||||
options.secureJsonData = {};
|
||||
}
|
||||
options.secureJsonData.password = password;
|
||||
}
|
||||
|
||||
onOptionsChange({
|
||||
...options,
|
||||
jsonData: {
|
||||
trends: true,
|
||||
trendsFrom: '',
|
||||
trendsRange: '',
|
||||
cacheTTL: '',
|
||||
timeout: undefined,
|
||||
disableDataAlignment: false,
|
||||
...restJsonData,
|
||||
},
|
||||
});
|
||||
|
||||
if (options.jsonData.dbConnectionEnable) {
|
||||
if (!options.jsonData.dbConnectionDatasourceId) {
|
||||
const dsName = options.jsonData.dbConnectionDatasourceName;
|
||||
getDataSourceSrv()
|
||||
.get(dsName)
|
||||
.then((ds) => {
|
||||
if (ds) {
|
||||
const selectedDs = getDirectDBDatasources().find((dsOption) => dsOption.id === ds.id);
|
||||
setSelectedDBDatasource({ label: selectedDs.name, value: selectedDs.id });
|
||||
setCurrentDSType(selectedDs.type);
|
||||
onOptionsChange({
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
dbConnectionDatasourceId: ds.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const selectedDs = getDirectDBDatasources().find(
|
||||
(dsOption) => dsOption.id === options.jsonData.dbConnectionDatasourceId
|
||||
);
|
||||
setSelectedDBDatasource({ label: selectedDs.name, value: selectedDs.id });
|
||||
setCurrentDSType(selectedDs.type);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataSourceHttpSettings
|
||||
defaultUrl={'http://localhost/zabbix/api_jsonrpc.php'}
|
||||
dataSourceConfig={options}
|
||||
showAccessOptions={true}
|
||||
onChange={onOptionsChange}
|
||||
/>
|
||||
|
||||
<div className="gf-form-group">
|
||||
<h3 className="page-heading">Zabbix API details</h3>
|
||||
<div className="gf-form max-width-25">
|
||||
<FormField
|
||||
labelWidth={7}
|
||||
inputWidth={15}
|
||||
label="Username"
|
||||
value={options.jsonData.username || ''}
|
||||
onChange={jsonDataChangeHandler('username', options, onOptionsChange)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="gf-form max-width-25">
|
||||
{options.secureJsonFields?.password ? (
|
||||
<>
|
||||
<FormField
|
||||
labelWidth={7}
|
||||
inputWidth={15}
|
||||
label="Password"
|
||||
disabled={true}
|
||||
value=""
|
||||
placeholder="Configured"
|
||||
/>
|
||||
<Button onClick={resetSecureJsonField('password', options, onOptionsChange)}>Reset</Button>
|
||||
</>
|
||||
) : (
|
||||
<FormField
|
||||
labelWidth={7}
|
||||
inputWidth={15}
|
||||
label="Password"
|
||||
type="password"
|
||||
value={options.secureJsonData?.password || options.jsonData.password || ''}
|
||||
onChange={secureJsonDataChangeHandler('password', options, onOptionsChange)}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<Switch
|
||||
label="Trends"
|
||||
labelClass="width-7"
|
||||
checked={options.jsonData.trends}
|
||||
onChange={jsonDataSwitchHandler('trends', options, onOptionsChange)}
|
||||
/>
|
||||
{options.jsonData.trends && (
|
||||
<>
|
||||
<div className="gf-form">
|
||||
<FormField
|
||||
labelWidth={7}
|
||||
inputWidth={4}
|
||||
label="After"
|
||||
value={options.jsonData.trendsFrom || ''}
|
||||
placeholder="7d"
|
||||
onChange={jsonDataChangeHandler('trendsFrom', options, onOptionsChange)}
|
||||
tooltip="Time after which trends will be used.
|
||||
Best practice is to set this value to your history storage period (7d, 30d, etc)."
|
||||
/>
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<FormField
|
||||
labelWidth={7}
|
||||
inputWidth={4}
|
||||
label="Range"
|
||||
value={options.jsonData.trendsRange || ''}
|
||||
placeholder="4d"
|
||||
onChange={jsonDataChangeHandler('trendsRange', options, onOptionsChange)}
|
||||
tooltip="Time range width after which trends will be used instead of history.
|
||||
It's better to set this value in range of 4 to 7 days to prevent loading large amount of history data."
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="gf-form">
|
||||
<FormField
|
||||
labelWidth={7}
|
||||
inputWidth={4}
|
||||
label="Cache TTL"
|
||||
value={options.jsonData.cacheTTL || ''}
|
||||
placeholder="1h"
|
||||
onChange={jsonDataChangeHandler('cacheTTL', options, onOptionsChange)}
|
||||
tooltip="Zabbix data source caches metric names in memory. Specify how often data will be updated."
|
||||
/>
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<FormField
|
||||
labelWidth={7}
|
||||
inputWidth={4}
|
||||
type="number"
|
||||
label="Timeout"
|
||||
value={options.jsonData.timeout}
|
||||
onChange={(event) => {
|
||||
onOptionsChange({
|
||||
...options,
|
||||
jsonData: { ...options.jsonData, timeout: parseInt(event.currentTarget.value, 10) },
|
||||
});
|
||||
}}
|
||||
tooltip="Zabbix API connection timeout in seconds. Default is 30."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="gf-form-group">
|
||||
<h3 className="page-heading">Direct DB Connection</h3>
|
||||
<Switch
|
||||
label="Enable"
|
||||
labelClass="width-9"
|
||||
checked={options.jsonData.dbConnectionEnable}
|
||||
onChange={jsonDataSwitchHandler('dbConnectionEnable', options, onOptionsChange)}
|
||||
/>
|
||||
{options.jsonData.dbConnectionEnable && (
|
||||
<>
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel width={9}>Data Source</InlineFormLabel>
|
||||
<Select
|
||||
width={32}
|
||||
options={getDirectDBDSOptions()}
|
||||
value={selectedDBDatasource}
|
||||
onChange={directDBDatasourceChanegeHandler(
|
||||
options,
|
||||
onOptionsChange,
|
||||
setSelectedDBDatasource,
|
||||
setCurrentDSType
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{currentDSType === 'influxdb' && (
|
||||
<div className="gf-form">
|
||||
<FormField
|
||||
labelWidth={9}
|
||||
inputWidth={16}
|
||||
label="Retention Policy"
|
||||
value={options.jsonData.dbConnectionRetentionPolicy || ''}
|
||||
placeholder="Retention policy name"
|
||||
onChange={jsonDataChangeHandler('dbConnectionRetentionPolicy', options, onOptionsChange)}
|
||||
tooltip="Specify retention policy name for fetching long-term stored data (optional).
|
||||
Leave it blank if only default retention policy used."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="gf-form-group">
|
||||
<h3 className="page-heading">Other</h3>
|
||||
<Switch
|
||||
label="Disable acknowledges for read-only users"
|
||||
labelClass="width-16"
|
||||
checked={options.jsonData.disableReadOnlyUsersAck}
|
||||
onChange={jsonDataSwitchHandler('disableReadOnlyUsersAck', options, onOptionsChange)}
|
||||
/>
|
||||
<Switch
|
||||
label="Disable data alignment"
|
||||
labelClass="width-16"
|
||||
checked={!!options.jsonData.disableDataAlignment}
|
||||
onChange={jsonDataSwitchHandler('disableDataAlignment', options, onOptionsChange)}
|
||||
tooltip="Data alignment feature aligns points based on item update interval.
|
||||
For instance, if value collected once per minute, then timestamp of the each point will be set to the start of corresponding minute.
|
||||
This alignment required for proper work of the stacked graphs.
|
||||
If you don't need stacked graphs and want to get exactly the same timestamps as in Zabbix, then you can disable this feature."
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const jsonDataChangeHandler =
|
||||
(
|
||||
key: keyof ZabbixDSOptions,
|
||||
value: DataSourceSettings<ZabbixDSOptions, ZabbixSecureJSONData>,
|
||||
onChange: Props['onOptionsChange']
|
||||
) =>
|
||||
(event: React.SyntheticEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||
onChange({
|
||||
...value,
|
||||
jsonData: {
|
||||
...value.jsonData,
|
||||
[key]: event.currentTarget.value,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const jsonDataSwitchHandler =
|
||||
(
|
||||
key: keyof ZabbixDSOptions,
|
||||
value: DataSourceSettings<ZabbixDSOptions, ZabbixSecureJSONData>,
|
||||
onChange: Props['onOptionsChange']
|
||||
) =>
|
||||
(event: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
onChange({
|
||||
...value,
|
||||
jsonData: {
|
||||
...value.jsonData,
|
||||
[key]: (event.target as HTMLInputElement).checked,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const secureJsonDataChangeHandler =
|
||||
(
|
||||
key: keyof ZabbixDSOptions,
|
||||
value: DataSourceSettings<ZabbixDSOptions, ZabbixSecureJSONData>,
|
||||
onChange: Props['onOptionsChange']
|
||||
) =>
|
||||
(event: React.SyntheticEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||
onChange({
|
||||
...value,
|
||||
secureJsonData: {
|
||||
...value.secureJsonData,
|
||||
[key]: event.currentTarget.value,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const resetSecureJsonField =
|
||||
(
|
||||
key: keyof ZabbixDSOptions,
|
||||
value: DataSourceSettings<ZabbixDSOptions, ZabbixSecureJSONData>,
|
||||
onChange: Props['onOptionsChange']
|
||||
) =>
|
||||
(event: React.SyntheticEvent<HTMLButtonElement>) => {
|
||||
onChange({
|
||||
...value,
|
||||
secureJsonFields: {
|
||||
...value.secureJsonFields,
|
||||
[key]: false,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const directDBDatasourceChanegeHandler =
|
||||
(
|
||||
options: DataSourceSettings<ZabbixDSOptions, ZabbixSecureJSONData>,
|
||||
onChange: Props['onOptionsChange'],
|
||||
setSelectedDS: React.Dispatch<any>,
|
||||
setSelectedDSType: React.Dispatch<any>
|
||||
) =>
|
||||
(value: SelectableValue<number>) => {
|
||||
const selectedDs = getDirectDBDatasources().find((dsOption) => dsOption.id === value.value);
|
||||
setSelectedDS({ label: selectedDs.name, value: selectedDs.id });
|
||||
setSelectedDSType(selectedDs.type);
|
||||
onChange({
|
||||
...options,
|
||||
jsonData: {
|
||||
...options.jsonData,
|
||||
dbConnectionDatasourceId: value.value,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const getDirectDBDatasources = () => {
|
||||
let dsList = (getDataSourceSrv() as any).getAll();
|
||||
dsList = dsList.filter((ds) => SUPPORTED_SQL_DS.includes(ds.type));
|
||||
return dsList;
|
||||
};
|
||||
|
||||
const getDirectDBDSOptions = () => {
|
||||
const dsList = getDirectDBDatasources();
|
||||
const dsOpts: Array<SelectableValue<number>> = dsList.map((ds) => ({
|
||||
label: ds.name,
|
||||
value: ds.id,
|
||||
description: ds.type,
|
||||
}));
|
||||
return dsOpts;
|
||||
};
|
||||
Reference in New Issue
Block a user