chore: bump @grafana/create-plugin configuration to 6.7.1 (#2167)

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
This commit is contained in:
ismail simsek
2026-01-08 15:56:29 +01:00
committed by GitHub
parent da27b9a917
commit 1bb5e8a5dd
24 changed files with 3500 additions and 3670 deletions

View File

@@ -1,3 +1,4 @@
{ {
"version": "5.26.4" "version": "6.7.1",
"features": {}
} }

View File

@@ -1,30 +0,0 @@
/*
* ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️
*
* In order to extend the configuration follow the steps in
* https://grafana.com/developers/plugin-tools/how-to-guides/extend-configurations#extend-the-eslint-config
*/
{
"extends": ["@grafana/eslint-config"],
"root": true,
"rules": {
"react/prop-types": "off"
},
"overrides": [
{
"files": ["src/**/*.{ts,tsx}"],
"rules": {
"@typescript-eslint/no-deprecated": "warn"
},
"parserOptions": {
"project": "./tsconfig.json"
}
},
{
"files": ["./tests/**/*"],
"rules": {
"react-hooks/rules-of-hooks": "off"
}
}
]
}

View File

@@ -0,0 +1,43 @@
import type { Configuration, ExternalItemFunctionData } from 'webpack';
type ExternalsType = Configuration['externals'];
export const externals: ExternalsType = [
// Required for dynamic publicPath resolution
{ 'amd-module': 'module' },
'lodash',
'jquery',
'moment',
'slate',
'emotion',
'@emotion/react',
'@emotion/css',
'prismjs',
'slate-plain-serializer',
'@grafana/slate-react',
'react',
'react-dom',
'react-redux',
'redux',
'rxjs',
'i18next',
'react-router',
'd3',
'angular',
/^@grafana\/ui/i,
/^@grafana\/runtime/i,
/^@grafana\/data/i,
// Mark legacy SDK imports as external if their name starts with the "grafana/" prefix
({ request }: ExternalItemFunctionData, callback: (error?: Error, result?: string) => void) => {
const prefix = 'grafana/';
const hasPrefix = (request: string) => request.indexOf(prefix) === 0;
const stripPrefix = (request: string) => request.slice(prefix.length);
if (request && hasPrefix(request)) {
return callback(undefined, stripPrefix(request));
}
callback();
},
];

View File

@@ -7,7 +7,7 @@ services:
context: . context: .
args: args:
grafana_image: ${GRAFANA_IMAGE:-grafana-enterprise} grafana_image: ${GRAFANA_IMAGE:-grafana-enterprise}
grafana_version: ${GRAFANA_VERSION:-12.1.1} grafana_version: ${GRAFANA_VERSION:-12.2.0}
development: ${DEVELOPMENT:-false} development: ${DEVELOPMENT:-false}
anonymous_auth_enabled: ${ANONYMOUS_AUTH_ENABLED:-true} anonymous_auth_enabled: ${ANONYMOUS_AUTH_ENABLED:-true}
ports: ports:

31
.config/eslint.config.mjs Normal file
View File

@@ -0,0 +1,31 @@
import { defineConfig } from 'eslint/config';
import grafanaConfig from '@grafana/eslint-config/flat.js';
export default defineConfig([
...grafanaConfig,
{
rules: {
'react/prop-types': 'off',
},
},
{
files: ['src/**/*.{ts,tsx}'],
languageOptions: {
parserOptions: {
project: './tsconfig.json',
},
},
rules: {
'@typescript-eslint/no-deprecated': 'warn',
},
},
{
files: ['./tests/**/*'],
rules: {
'react-hooks/rules-of-hooks': 'off',
},
},
]);

1
.config/types/setupTests.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
import '@testing-library/jest-dom';

View File

@@ -19,6 +19,7 @@ import VirtualModulesPlugin from 'webpack-virtual-modules';
import { BuildModeWebpackPlugin } from './BuildModeWebpackPlugin.ts'; import { BuildModeWebpackPlugin } from './BuildModeWebpackPlugin.ts';
import { DIST_DIR, SOURCE_DIR } from './constants.ts'; import { DIST_DIR, SOURCE_DIR } from './constants.ts';
import { getCPConfigVersion, getEntries, getPackageJson, getPluginJson, hasReadme, isWSL } from './utils.ts'; import { getCPConfigVersion, getEntries, getPackageJson, getPluginJson, hasReadme, isWSL } from './utils.ts';
import { externals } from '../bundler/externals.ts';
const pluginJson = getPluginJson(); const pluginJson = getPluginJson();
const cpVersion = getCPConfigVersion(); const cpVersion = getCPConfigVersion();
@@ -55,45 +56,7 @@ const config = async (env: Env): Promise<Configuration> => {
entry: await getEntries(), entry: await getEntries(),
externals: [ externals,
// Required for dynamic publicPath resolution
{ 'amd-module': 'module' },
'lodash',
'jquery',
'moment',
'slate',
'emotion',
'@emotion/react',
'@emotion/css',
'prismjs',
'slate-plain-serializer',
'@grafana/slate-react',
'react',
'react-dom',
'react-redux',
'redux',
'rxjs',
'i18next',
'react-router',
'd3',
'angular',
/^@grafana\/ui/i,
/^@grafana\/runtime/i,
/^@grafana\/data/i,
// Mark legacy SDK imports as external if their name starts with the "grafana/" prefix
({ request }, callback) => {
const prefix = 'grafana/';
const hasPrefix = (request: string) => request.indexOf(prefix) === 0;
const stripPrefix = (request: string) => request.substr(prefix.length);
if (request && hasPrefix(request)) {
return callback(undefined, stripPrefix(request));
}
callback();
},
],
// Support WebAssembly according to latest spec - makes WebAssembly module async // Support WebAssembly according to latest spec - makes WebAssembly module async
experiments: { experiments: {
@@ -224,7 +187,8 @@ const config = async (env: Env): Promise<Configuration> => {
new ReplaceInFileWebpackPlugin([ new ReplaceInFileWebpackPlugin([
{ {
dir: DIST_DIR, dir: DIST_DIR,
files: ['plugin.json', 'README.md'], test: [/(^|\/)plugin\.json$/, /(^|\/)README\.md$/],
rules: [ rules: [
{ {
search: /\%VERSION\%/g, search: /\%VERSION\%/g,

View File

@@ -1,9 +0,0 @@
{
"extends": "./.config/.eslintrc",
"plugins": ["prettier"],
"ignorePatterns": ["/src/test-setup/**/*"],
"rules": {
"react-hooks/exhaustive-deps": "off",
"prettier/prettier": "error"
}
}

View File

@@ -1,4 +1,4 @@
module.exports = { module.exports = {
// Prettier configuration provided by Grafana scaffolding // Prettier configuration provided by Grafana scaffolding
...require("./.config/.prettierrc.js") ...require('./.config/.prettierrc.js'),
}; };

60
eslint.config.mjs Normal file
View File

@@ -0,0 +1,60 @@
import { defineConfig } from 'eslint/config';
import baseConfig from './.config/eslint.config.mjs';
import prettier from 'eslint-plugin-prettier';
export default defineConfig([
{
ignores: [
'**/*.sublime-workspace',
'**/*.sublime-project',
'**/.idea/',
'**/.vscode',
'**/*.bat',
'**/.DS_Store',
'docs/site/',
'dist/test/',
'dist/test-setup/',
'**/vendor',
'src/vendor',
'src/vendor/npm',
'**/node_modules',
'**/coverage/',
'tmp',
'**/artifacts/',
'**/work/',
'**/test-results/',
'**/playwright-report/',
'**/blob-report/',
'playwright/.cache/',
'playwright/.auth/',
'**/npm-debug.log',
'**/yarn-error.log',
'**/dist/',
'**/ci/',
'**/alexanderzobnin-zabbix-app.zip',
'**/.eslintcache',
'public/css/*.min.css',
'**/provisioning/',
'devenv/nginx/nginx.crt',
'devenv/nginx/nginx.key',
'**/.pnp.*',
'.yarn/*',
'!.yarn/patches',
'!.yarn/plugins',
'!.yarn/releases',
'!.yarn/sdks',
'!.yarn/versions',
],
},
...baseConfig,
{
plugins: {
prettier: prettier,
},
rules: {
'react-hooks/exhaustive-deps': 'off',
'prettier/prettier': 'error',
},
},
]);

View File

@@ -16,7 +16,7 @@
"build": "webpack -c ./webpack.config.ts --env production", "build": "webpack -c ./webpack.config.ts --env production",
"dev": "webpack -w -c ./webpack.config.ts --env development", "dev": "webpack -w -c ./webpack.config.ts --env development",
"e2e": "playwright test", "e2e": "playwright test",
"lint": "eslint --cache --ignore-path ./.gitignore --ext .js,.jsx,.ts,.tsx .", "lint": "eslint --cache .",
"lint:fix": "yarn run lint --fix && prettier --write --list-different .", "lint:fix": "yarn run lint --fix && prettier --write --list-different .",
"server": "docker compose up --build", "server": "docker compose up --build",
"server:down": "docker compose --file ./devenv/default/docker-compose.yml down", "server:down": "docker compose --file ./devenv/default/docker-compose.yml down",
@@ -28,89 +28,84 @@
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@emotion/css": "11.10.6", "@emotion/css": "11.13.5",
"@grafana/data": "^12.1.0", "@grafana/data": "12.3.1",
"@grafana/i18n": "^12.1.0", "@grafana/i18n": "12.3.1",
"@grafana/plugin-ui": "^0.10.10", "@grafana/plugin-ui": "0.12.1",
"@grafana/runtime": "^12.1.0", "@grafana/runtime": "12.3.1",
"@grafana/schema": "^12.1.0", "@grafana/schema": "12.3.1",
"@grafana/ui": "^12.1.0", "@grafana/ui": "12.3.1",
"react": "18.3.1", "react": "18.3.1",
"react-dom": "18.3.1", "react-dom": "18.3.1",
"react-router-dom": "^6.22.0", "react-use": "17.6.0",
"rxjs": "7.8.2", "rxjs": "7.8.2",
"tslib": "2.5.3" "tslib": "2.8.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.28.4", "@babel/core": "7.28.5",
"@changesets/cli": "^2.29.7", "@changesets/cli": "2.29.8",
"@grafana/e2e-selectors": "12.1.0", "@grafana/e2e-selectors": "12.3.1",
"@grafana/eslint-config": "^8.0.0", "@grafana/eslint-config": "9.0.0",
"@grafana/plugin-e2e": "^2.2.3", "@grafana/plugin-e2e": "3.1.1",
"@grafana/tsconfig": "^2.0.0", "@grafana/tsconfig": "2.0.1",
"@playwright/test": "^1.55.0", "@playwright/test": "1.57.0",
"@stylistic/eslint-plugin-ts": "^2.9.0", "@stylistic/eslint-plugin-ts": "4.4.1",
"@swc/core": "^1.3.90", "@swc/core": "1.15.8",
"@swc/helpers": "^0.5.0", "@swc/helpers": "0.5.18",
"@swc/jest": "^0.2.26", "@swc/jest": "0.2.39",
"@tanstack/react-table": "^8.21.3", "@tanstack/react-table": "8.21.3",
"@testing-library/jest-dom": "6.1.4", "@testing-library/dom": "^10.4.1",
"@testing-library/react": "14.0.0", "@testing-library/jest-dom": "6.9.1",
"@types/glob": "^8.0.0", "@testing-library/react": "16.3.1",
"@types/grafana": "github:CorpGlory/types-grafana", "@types/grafana": "github:CorpGlory/types-grafana",
"@types/jest": "^29.5.0", "@types/jest": "30.0.0",
"@types/lodash": "^4.14.194", "@types/lodash": "4.17.21",
"@types/node": "^20.19.16", "@types/node": "25.0.3",
"@types/react": "18.3.24", "@types/react": "18.3.27",
"@types/react-router-dom": "^5.2.0", "@types/react-dom": "18.3.1",
"@types/testing-library__jest-dom": "5.14.8", "@typescript-eslint/eslint-plugin": "8.52.0",
"@typescript-eslint/eslint-plugin": "^8.3.0", "@typescript-eslint/parser": "8.52.0",
"@typescript-eslint/parser": "^8.3.0", "autoprefixer": "10.4.23",
"autoprefixer": "10.4.7", "copy-webpack-plugin": "13.0.1",
"clean-webpack-plugin": "^0.1.19", "cspell": "9.4.0",
"copy-webpack-plugin": "^11.0.0", "css-loader": "7.1.2",
"cspell": "6.31.3", "eslint": "9.39.2",
"css-loader": "^6.7.3", "eslint-config-prettier": "10.1.8",
"eslint": "8.57.1", "eslint-plugin-jsdoc": "61.5.0",
"eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "5.5.4",
"eslint-plugin-deprecation": "^2.0.0", "eslint-plugin-react": "7.37.5",
"eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-react-hooks": "7.0.1",
"eslint-plugin-prettier": "^5.0.0", "eslint-webpack-plugin": "5.0.2",
"eslint-plugin-react": "^7.33.2", "fork-ts-checker-webpack-plugin": "9.1.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-webpack-plugin": "^4.0.1",
"fork-ts-checker-webpack-plugin": "^8.0.0",
"glob": "13.0.0", "glob": "13.0.0",
"identity-obj-proxy": "3.0.0", "identity-obj-proxy": "3.0.0",
"imports-loader": "^5.0.0", "imports-loader": "5.0.0",
"jest": "^29.5.0", "jest": "30.2.0",
"jest-environment-jsdom": "^29.5.0", "jest-environment-jsdom": "30.2.0",
"lodash": "4.17.21", "lodash": "4.17.21",
"mini-css-extract-plugin": "2.6.1", "mini-css-extract-plugin": "2.9.4",
"moment": "2.29.4", "moment": "2.30.1",
"postcss": "8.4.31", "postcss": "8.5.6",
"postcss-loader": "7.0.1", "postcss-loader": "8.2.0",
"postcss-reporter": "7.0.5", "postcss-reporter": "7.1.0",
"postcss-scss": "4.0.4", "postcss-scss": "4.0.9",
"prettier": "^3.0.3", "prettier": "3.7.4",
"prop-types": "15.7.2", "replace-in-file-webpack-plugin": "1.0.6",
"react-use": "17.4.0", "sass": "1.97.2",
"replace-in-file-webpack-plugin": "^1.0.6", "sass-loader": "16.0.6",
"sass": "1.63.2", "semver": "7.7.3",
"sass-loader": "13.3.3", "style-loader": "4.0.0",
"semver": "^7.7.2", "swc-loader": "0.2.6",
"style-loader": "3.3.4", "terser-webpack-plugin": "5.3.16",
"swc-loader": "^0.2.3", "ts-node": "10.9.2",
"terser-webpack-plugin": "^5.3.14", "tsconfig-paths": "4.2.0",
"ts-node": "^10.9.2", "typescript": "5.9.3",
"tsconfig-paths": "^4.2.0", "webpack": "5.104.1",
"typescript": "5.5.4", "webpack-cli": "6.0.1",
"webpack": "^5.94.0", "webpack-livereload-plugin": "3.0.2",
"webpack-cli": "^5.1.4", "webpack-remove-empty-scripts": "1.1.1",
"webpack-livereload-plugin": "^3.0.2", "webpack-subresource-integrity": "5.1.0",
"webpack-remove-empty-scripts": "^1.0.1", "webpack-virtual-modules": "0.6.2"
"webpack-subresource-integrity": "^5.1.0",
"webpack-virtual-modules": "^0.6.2"
}, },
"resolutions": { "resolutions": {
"glob": "^13.0.0", "glob": "^13.0.0",

View File

@@ -45,7 +45,7 @@ export class ModalController extends React.Component<Props, State> {
}); });
}; };
renderModal() { renderModal(): React.ReactNode {
const { component, props } = this.state; const { component, props } = this.state;
if (!component) { if (!component) {
return null; return null;
@@ -53,7 +53,7 @@ export class ModalController extends React.Component<Props, State> {
this.modalRoot.appendChild(this.modalNode); this.modalRoot.appendChild(this.modalNode);
const modal = React.createElement(provideTheme(component), props); const modal = React.createElement(provideTheme(component), props);
return ReactDOM.createPortal(modal, this.modalNode); return ReactDOM.createPortal(modal, this.modalNode) as React.ReactNode;
} }
render() { render() {

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useMemo } from 'react';
import { getDataSourceSrv, config } from '@grafana/runtime'; import { getDataSourceSrv, config } from '@grafana/runtime';
import { DataSourcePluginOptionsEditorProps, DataSourceSettings, GrafanaTheme2, SelectableValue } from '@grafana/data'; import { DataSourcePluginOptionsEditorProps, DataSourceSettings, GrafanaTheme2, SelectableValue } from '@grafana/data';
import { import {
@@ -42,8 +42,19 @@ export const ConfigEditor = (props: Props) => {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const { options, onOptionsChange } = props; const { options, onOptionsChange } = props;
const [selectedDBDatasource, setSelectedDBDatasource] = useState(null); // Derive selectedDBDatasource and currentDSType from options
const [currentDSType, setCurrentDSType] = useState(''); const { selectedDBDatasource, currentDSType } = useMemo(() => {
if (!options.jsonData.dbConnectionEnable || !options.jsonData.dbConnectionDatasourceId) {
return { selectedDBDatasource: null, currentDSType: '' };
}
const selectedDs = getDirectDBDatasources().find(
(dsOption) => dsOption.id === options.jsonData.dbConnectionDatasourceId
);
return {
selectedDBDatasource: selectedDs ? { label: selectedDs.name, value: selectedDs.id } : null,
currentDSType: selectedDs?.type || '',
};
}, [options.jsonData.dbConnectionEnable, options.jsonData.dbConnectionDatasourceId]);
// Apply some defaults on initial render // Apply some defaults on initial render
useEffect(() => { useEffect(() => {
@@ -73,32 +84,22 @@ export const ConfigEditor = (props: Props) => {
secureJsonData: { ...newSecureJsonData }, secureJsonData: { ...newSecureJsonData },
}); });
if (options.jsonData.dbConnectionEnable) { // Handle async lookup when dbConnectionDatasourceId is not set but name is available
if (!options.jsonData.dbConnectionDatasourceId) { if (options.jsonData.dbConnectionEnable && !options.jsonData.dbConnectionDatasourceId) {
const dsName = options.jsonData.dbConnectionDatasourceName; const dsName = options.jsonData.dbConnectionDatasourceName;
getDataSourceSrv() getDataSourceSrv()
.get(dsName) .get(dsName)
.then((ds) => { .then((ds) => {
if (ds) { if (ds) {
const selectedDs = getDirectDBDatasources().find((dsOption) => dsOption.id === ds.id); onOptionsChange({
setSelectedDBDatasource({ label: selectedDs?.name, value: selectedDs?.id }); ...options,
setCurrentDSType(selectedDs?.type); jsonData: {
onOptionsChange({ ...options.jsonData,
...options, dbConnectionDatasourceId: ds.id,
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);
}
} }
}, []); }, []);
@@ -318,12 +319,7 @@ export const ConfigEditor = (props: Props) => {
width={40} width={40}
value={selectedDBDatasource} value={selectedDBDatasource}
options={getDirectDBDSOptions()} options={getDirectDBDSOptions()}
onChange={directDBDatasourceChanegeHandler( onChange={directDBDatasourceChanegeHandler(options, onOptionsChange)}
options,
onOptionsChange,
setSelectedDBDatasource,
setCurrentDSType
)}
placeholder="Select a DB datasource (MySQL, PostgreSQL, InfluxDB)" placeholder="Select a DB datasource (MySQL, PostgreSQL, InfluxDB)"
/> />
</Field> </Field>
@@ -480,16 +476,8 @@ const resetSecureJsonField =
}; };
const directDBDatasourceChanegeHandler = const directDBDatasourceChanegeHandler =
( (options: DataSourceSettings<ZabbixDSOptions, ZabbixSecureJSONData>, onChange: Props['onOptionsChange']) =>
options: DataSourceSettings<ZabbixDSOptions, ZabbixSecureJSONData>,
onChange: Props['onOptionsChange'],
setSelectedDS: React.Dispatch<any>,
setSelectedDSType: React.Dispatch<any>
) =>
(value: SelectableValue<number>) => { (value: SelectableValue<number>) => {
const selectedDs = getDirectDBDatasources().find((dsOption) => dsOption.id === value.value);
setSelectedDS({ label: selectedDs.name, value: selectedDs.id });
setSelectedDSType(selectedDs.type);
onChange({ onChange({
...options, ...options,
jsonData: { jsonData: {

View File

@@ -111,8 +111,11 @@ function getProblemsQueryDefaults(): Partial<ZabbixMetricsQuery> {
}; };
} }
export interface ZabbixQueryEditorProps export interface ZabbixQueryEditorProps extends QueryEditorProps<
extends QueryEditorProps<ZabbixDatasource, ZabbixMetricsQuery, ZabbixDSOptions> {} ZabbixDatasource,
ZabbixMetricsQuery,
ZabbixDSOptions
> {}
export const QueryEditor = ({ query, datasource, onChange, onRunQuery, range }: ZabbixQueryEditorProps) => { export const QueryEditor = ({ query, datasource, onChange, onRunQuery, range }: ZabbixQueryEditorProps) => {
const [itemCount, setItemCount] = useState(0); const [itemCount, setItemCount] = useState(0);

View File

@@ -14,10 +14,14 @@ export interface Props {
export const QueryFunctionsEditor = ({ query, onChange }: Props) => { export const QueryFunctionsEditor = ({ query, onChange }: Props) => {
const onFuncParamChange = (func: MetricFunc, index: number, value: string) => { const onFuncParamChange = (func: MetricFunc, index: number, value: string) => {
func.params[index] = value; const functions = query.functions.map((f) => {
const funcIndex = query.functions.findIndex((f) => f === func); if (f === func) {
const functions = query.functions; const newParams = [...f.params];
functions[funcIndex] = func; newParams[index] = value;
return { ...f, params: newParams };
}
return f;
});
onChange({ ...query, functions }); onChange({ ...query, functions });
}; };

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react'; import { useMemo } from 'react';
import { ScopedVars } from '@grafana/data'; import { ScopedVars } from '@grafana/data';
import { ZabbixDatasource } from '../datasource'; import { ZabbixDatasource } from '../datasource';
import { ZabbixMetricsQuery } from '../types/query'; import { ZabbixMetricsQuery } from '../types/query';
@@ -10,12 +10,10 @@ export const useInterpolatedQuery = (
query: ZabbixMetricsQuery, query: ZabbixMetricsQuery,
scopedVars?: ScopedVars scopedVars?: ScopedVars
): ZabbixMetricsQuery => { ): ZabbixMetricsQuery => {
const [interpolatedQuery, setInterpolatedQuery] = useState<ZabbixMetricsQuery>(query); const resolvedScopedVars = scopedVars ?? EMPTY_SCOPED_VARS;
const resolvedScopedVars = useMemo(() => scopedVars ?? EMPTY_SCOPED_VARS, [scopedVars]);
useEffect(() => { const interpolatedQuery = useMemo(() => {
const replacedQuery = datasource.interpolateVariablesInQueries([query], resolvedScopedVars)[0]; return datasource.interpolateVariablesInQueries([query], resolvedScopedVars)[0];
setInterpolatedQuery(replacedQuery);
}, [datasource, query, resolvedScopedVars]); }, [datasource, query, resolvedScopedVars]);
return interpolatedQuery; return interpolatedQuery;

View File

@@ -13,7 +13,6 @@ jest.mock(
// Provide a custom query implementation that resolves backend + frontend + db + annotations // Provide a custom query implementation that resolves backend + frontend + db + annotations
// so tests relying on merged results receive expected data. // so tests relying on merged results receive expected data.
if (actual && actual.DataSourceWithBackend && actual.DataSourceWithBackend.prototype) { if (actual && actual.DataSourceWithBackend && actual.DataSourceWithBackend.prototype) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
actual.DataSourceWithBackend.prototype.query = function (request: any) { actual.DataSourceWithBackend.prototype.query = function (request: any) {
const that: any = this; const that: any = this;

View File

@@ -29,9 +29,7 @@ export const ProblemsPanel = (props: ProblemsPanelProps) => {
for (const dataFrame of data.series) { for (const dataFrame of data.series) {
try { try {
const values = dataFrame.fields[0].values; const values = dataFrame.fields[0].values;
if (values.toArray) { problems.push(...values);
problems.push(...values.toArray());
}
} catch (error) { } catch (error) {
console.log(error); console.log(error);
return []; return [];
@@ -125,7 +123,8 @@ export const ProblemsPanel = (props: ProblemsPanelProps) => {
}; };
const addTagFilter = (tag: ZBXTag, datasource: DataSourceRef) => { const addTagFilter = (tag: ZBXTag, datasource: DataSourceRef) => {
const targets = data.request?.targets!; const originalTargets = data.request?.targets!;
const targets = _.cloneDeep(originalTargets);
let updated = false; let updated = false;
for (const target of targets) { for (const target of targets) {
if (target.datasource?.uid === datasource?.uid || target.datasource === datasource) { if (target.datasource?.uid === datasource?.uid || target.datasource === datasource) {
@@ -148,7 +147,8 @@ export const ProblemsPanel = (props: ProblemsPanelProps) => {
const removeTagFilter = (tag: ZBXTag, datasource: DataSourceRef) => { const removeTagFilter = (tag: ZBXTag, datasource: DataSourceRef) => {
const matchTag = (t: ZBXTag) => t.tag === tag.tag && t.value === tag.value; const matchTag = (t: ZBXTag) => t.tag === tag.tag && t.value === tag.value;
const targets = data.request?.targets!; const originalTargets = data.request?.targets!;
const targets = _.cloneDeep(originalTargets);
let updated = false; let updated = false;
for (const target of targets) { for (const target of targets) {
if (target.datasource?.uid === datasource?.uid || target.datasource === datasource) { if (target.datasource?.uid === datasource?.uid || target.datasource === datasource) {
@@ -170,8 +170,8 @@ export const ProblemsPanel = (props: ProblemsPanelProps) => {
const getProblemEvents = async (problem: ProblemDTO) => { const getProblemEvents = async (problem: ProblemDTO) => {
const triggerids = [problem.triggerid]; const triggerids = [problem.triggerid];
const timeFrom = Math.ceil(dateMath.parse(timeRange.from)!.unix()); const timeFrom = Math.ceil(dateMath.toDateTime(timeRange.from, {}).unix());
const timeTo = Math.ceil(dateMath.parse(timeRange.to)!.unix()); const timeTo = Math.ceil(dateMath.toDateTime(timeRange.to, {}).unix());
const ds: any = await getDataSourceSrv().get(problem.datasource); const ds: any = await getDataSourceSrv().get(problem.datasource);
return ds.zabbix.getEvents(triggerids, timeFrom, timeTo, [0, 1], PROBLEM_EVENTS_LIMIT); return ds.zabbix.getEvents(triggerids, timeFrom, timeTo, [0, 1], PROBLEM_EVENTS_LIMIT);
}; };

View File

@@ -206,6 +206,7 @@ export class AckModalUnthemed extends PureComponent<Props, State> {
return ( return (
<Modal <Modal
isOpen={true} isOpen={true}
ariaLabel="Acknowledge problem"
onDismiss={this.dismiss} onDismiss={this.dismiss}
className={styles.modal} className={styles.modal}
title={ title={

View File

@@ -136,6 +136,7 @@ export class ExecScriptModalUnthemed extends PureComponent<Props, State> {
isOpen={true} isOpen={true}
onDismiss={this.dismiss} onDismiss={this.dismiss}
className={styles.modal} className={styles.modal}
ariaLabel="Execute script"
title={ title={
<div className={styles.modalHeaderTitle}> <div className={styles.modalHeaderTitle}>
{this.state.loading ? <Spinner size={18} /> : <FAIcon icon="terminal" />} {this.state.loading ? <Spinner size={18} /> : <FAIcon icon="terminal" />}

View File

@@ -53,26 +53,22 @@ export const ProblemDetails = ({
const [show, setShow] = useState(false); const [show, setShow] = useState(false);
useEffect(() => { useEffect(() => {
if (showTimeline) { const fetchData = async () => {
fetchProblemEvents(); const problem = original;
} if (showTimeline) {
fetchProblemAlerts(); const eventsData = await getProblemEvents(problem);
setEvents(eventsData);
}
const alertsData = await getProblemAlerts(problem);
setAletrs(alertsData);
};
fetchData();
requestAnimationFrame(() => { requestAnimationFrame(() => {
setShow(true); setShow(true);
}); });
}, []); }, [original, showTimeline, getProblemEvents, getProblemAlerts]);
const fetchProblemEvents = async () => {
const problem = original;
const events = await getProblemEvents(problem);
setEvents(events);
};
const fetchProblemAlerts = async () => {
const problem = original;
const alerts = await getProblemAlerts(problem);
setAletrs(alerts);
};
const handleTagClick = (tag: ZBXTag, datasource: DataSourceRef | string, ctrlKey?: boolean, shiftKey?: boolean) => { const handleTagClick = (tag: ZBXTag, datasource: DataSourceRef | string, ctrlKey?: boolean, shiftKey?: boolean) => {
if (onTagClick) { if (onTagClick) {

View File

@@ -1,17 +1,24 @@
import _ from 'lodash'; import _ from 'lodash';
// eslint-disable-next-line no-restricted-imports
import moment from 'moment'; import moment from 'moment';
var units = ['y', 'M', 'w', 'd', 'h', 'm', 's']; let units = ['y', 'M', 'w', 'd', 'h', 'm', 's'];
export function parse(text, roundUp) { export function parse(text, roundUp) {
if (!text) { return undefined; } if (!text) {
if (moment.isMoment(text)) { return text; } return undefined;
if (_.isDate(text)) { return moment(text); } }
if (moment.isMoment(text)) {
return text;
}
if (_.isDate(text)) {
return moment(text);
}
var time; let time;
var mathString = ''; let mathString = '';
var index; let index;
var parseString; let parseString;
if (text.substring(0, 3) === 'now') { if (text.substring(0, 3) === 'now') {
time = moment(); time = moment();
@@ -37,7 +44,7 @@ export function parse(text, roundUp) {
} }
export function isValid(text) { export function isValid(text) {
var date = parse(text); let date = parse(text);
if (!date) { if (!date) {
return false; return false;
} }
@@ -50,15 +57,15 @@ export function isValid(text) {
} }
export function parseDateMath(mathString, time, roundUp) { export function parseDateMath(mathString, time, roundUp) {
var dateTime = time; let dateTime = time;
var i = 0; let i = 0;
var len = mathString.length; let len = mathString.length;
while (i < len) { while (i < len) {
var c = mathString.charAt(i++); let c = mathString.charAt(i++);
var type; let type;
var num; let num;
var unit; let unit;
if (c === '/') { if (c === '/') {
type = 0; type = 0;
@@ -75,10 +82,12 @@ export function parseDateMath(mathString, time, roundUp) {
} else if (mathString.length === 2) { } else if (mathString.length === 2) {
num = mathString.charAt(i); num = mathString.charAt(i);
} else { } else {
var numFrom = i; let numFrom = i;
while (!isNaN(mathString.charAt(i))) { while (!isNaN(mathString.charAt(i))) {
i++; i++;
if (i > 10) { return undefined; } if (i > 10) {
return undefined;
}
} }
num = parseInt(mathString.substring(numFrom, i), 10); num = parseInt(mathString.substring(numFrom, i), 10);
} }

View File

@@ -26,24 +26,19 @@ export class PanelCtrl {
this.timing = {}; this.timing = {};
this.events = { this.events = {
on: () => {}, on: () => {},
emit: () => {} emit: () => {},
}; };
} }
init() { init() {}
}
renderingCompleted() { renderingCompleted() {}
}
refresh() { refresh() {}
}
publishAppEvent(evtName, evt) { publishAppEvent(evtName, evt) {}
}
changeView(fullscreen, edit) { changeView(fullscreen, edit) {}
}
viewPanel() { viewPanel() {
this.changeView(true, false); this.changeView(true, false);
@@ -57,14 +52,11 @@ export class PanelCtrl {
this.changeView(false, false); this.changeView(false, false);
} }
initEditMode() { initEditMode() {}
}
changeTab(newIndex) { changeTab(newIndex) {}
}
addEditorTab(title, directiveFn, index) { addEditorTab(title, directiveFn, index) {}
}
getMenu() { getMenu() {
return []; return [];
@@ -78,41 +70,29 @@ export class PanelCtrl {
return false; return false;
} }
calculatePanelHeight() { calculatePanelHeight() {}
}
render(payload) { render(payload) {}
}
toggleEditorHelp(index) { toggleEditorHelp(index) {}
}
duplicate() { duplicate() {}
}
updateColumnSpan(span) { updateColumnSpan(span) {}
}
removePanel() { removePanel() {}
}
editPanelJson() { editPanelJson() {}
}
replacePanel(newPanel, oldPanel) { replacePanel(newPanel, oldPanel) {}
}
sharePanel() { sharePanel() {}
}
getInfoMode() { getInfoMode() {}
}
getInfoContent(options) { getInfoContent(options) {}
}
openInspector() { openInspector() {}
}
} }
export class MetricsPanelCtrl extends PanelCtrl { export class MetricsPanelCtrl extends PanelCtrl {

6537
yarn.lock

File diff suppressed because it is too large Load Diff