From 5285d34cc019c77f7eee2e00a2afd8ff6e84a606 Mon Sep 17 00:00:00 2001 From: AJ Tomko <andrew.j.tomko@gmail.com> Date: Wed, 19 Oct 2022 08:59:24 -0400 Subject: [PATCH] Dashboard: Alerts user to incorrect tag format for JSON import (#54657) * Dashboard: Alerts user to incorrect tag format for JSON import Fixes #54285: Malformed tags cause hidden title and settings page crash * Update public/app/features/manage-dashboards/utils/validation.ts Co-authored-by: Polina Boneva <13227501+polibb@users.noreply.github.com> * Included Suggestions - Removed Comments - Updated Code Block accordingly - Updated Tests to camelCase over snake_case * Updates per comments - Re-wrapped function in try{}, catch{} as I appear to have overlooked including it in the initial refactor - Re-worded errors to align with initial error - Added a test case for invalid json * Update validation.ts Updated errors to read correctly to the root cause. Updated dashboard variable as const. * Update actions.test.ts Fix tests according to error output rewording * Update validation.ts - Included test for an empty string of non-array * Update actions.test.ts -- Commented incorrect commit for validation.ts, update: - Refactored code to better align and separate from generic JSON package tests followed by our manual checks of (1) Is array, and (2) if array, is of strings - Test cases now include a check for non-array empty string in the tag property Co-authored-by: Polina Boneva <13227501+polibb@users.noreply.github.com> --- .../manage-dashboards/state/actions.test.ts | 31 +++++++++++++++++++ .../manage-dashboards/utils/validation.ts | 15 +++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/public/app/features/manage-dashboards/state/actions.test.ts b/public/app/features/manage-dashboards/state/actions.test.ts index d6c366ed545..c37192e18ee 100644 --- a/public/app/features/manage-dashboards/state/actions.test.ts +++ b/public/app/features/manage-dashboards/state/actions.test.ts @@ -2,6 +2,8 @@ import { thunkTester } from 'test/core/thunk/thunkTester'; import { setBackendSrv } from '@grafana/runtime'; +import { validateDashboardJson } from '../utils/validation'; + import { importDashboard } from './actions'; import { DataSourceInput, ImportDashboardDTO, initialImportDashboardState, InputType } from './reducers'; @@ -75,3 +77,32 @@ describe('importDashboard', () => { }); }); }); + +describe('validateDashboardJson', () => { + it('Should return true if correct json', async () => { + const jsonImportCorrectFormat = '{"title": "Correct Format", "tags": ["tag1", "tag2"], "schemaVersion": 36}'; + const validateDashboardJsonCorrectFormat = await validateDashboardJson(jsonImportCorrectFormat); + expect(validateDashboardJsonCorrectFormat).toBe(true); + }); + it('Should not return true if nested tags', async () => { + const jsonImportNestedTags = + '{"title": "Nested tags","tags": ["tag1", "tag2", ["nestedTag1", "nestedTag2"]],"schemaVersion": 36}'; + const validateDashboardJsonNestedTags = await validateDashboardJson(jsonImportNestedTags); + expect(validateDashboardJsonNestedTags).toBe('tags expected array of strings'); + }); + it('Should not return true if not an array', async () => { + const jsonImportNotArray = '{"title": "Not Array","tags": "tag1","schemaVersion":36}'; + const validateDashboardJsonNotArray = await validateDashboardJson(jsonImportNotArray); + expect(validateDashboardJsonNotArray).toBe('tags expected array'); + }); + it('Should not return true if not an array and is blank string', async () => { + const jsonImportEmptyTags = '{"schemaVersion": 36,"tags": "", "title": "Empty Tags"}'; + const validateDashboardJsonEmptyTags = await validateDashboardJson(jsonImportEmptyTags); + expect(validateDashboardJsonEmptyTags).toBe('tags expected array'); + }); + it('Should not return true if not valid JSON', async () => { + const jsonImportInvalidJson = '{"schemaVersion": 36,"tags": {"tag", "nested tag"}, "title": "Nested lists"}'; + const validateDashboardJsonNotValid = await validateDashboardJson(jsonImportInvalidJson); + expect(validateDashboardJsonNotValid).toBe('Not valid JSON'); + }); +}); diff --git a/public/app/features/manage-dashboards/utils/validation.ts b/public/app/features/manage-dashboards/utils/validation.ts index bee8a2200c8..282b011cb00 100644 --- a/public/app/features/manage-dashboards/utils/validation.ts +++ b/public/app/features/manage-dashboards/utils/validation.ts @@ -3,12 +3,23 @@ import { getBackendSrv } from '@grafana/runtime'; import { validationSrv } from '../services/ValidationSrv'; export const validateDashboardJson = (json: string) => { + let dashboard; try { - JSON.parse(json); - return true; + dashboard = JSON.parse(json); } catch (error) { return 'Not valid JSON'; } + if (dashboard && dashboard.hasOwnProperty('tags')) { + if (Array.isArray(dashboard.tags)) { + const hasInvalidTag = dashboard.tags.some((tag: string) => typeof tag !== 'string'); + if (hasInvalidTag) { + return 'tags expected array of strings'; + } + } else { + return 'tags expected array'; + } + } + return true; }; export const validateGcomDashboard = (gcomDashboard: string) => {