From f86abf096d856b0fadccec7babee139953238d2b Mon Sep 17 00:00:00 2001 From: sam boyer Date: Tue, 20 Dec 2022 09:04:14 -0500 Subject: [PATCH] schema: Use generated dashboard model in frontend (#55769) Co-authored-by: Ivan Ortega Co-authored-by: Josh Hunt Co-authored-by: Dominik Prokop Co-authored-by: polinaboneva --- .betterer.results | 26 +-- .../alerting/testdata_alerts.json | 40 +---- kinds/structured/dashboard/dashboard_kind.cue | 89 +++++++++- .../grafana-data/src/types/templateVars.ts | 3 +- packages/grafana-schema/src/index.gen.ts | 32 ++-- .../raw/dashboard/x/dashboard_types.gen.ts | 149 +++++++++++++++-- .../src/veneer/dashboard.types.ts | 47 ++++-- pkg/codegen/jenny_tsveneerindex.go | 24 ++- pkg/kinds/dashboard/dashboard_types_gen.go | 153 ++++++++++++++++-- .../features/alerting/TestRuleResult.test.tsx | 7 +- .../NewRuleFromPanelButton.test.tsx | 5 +- .../rule-editor/AnnotationsField.test.tsx | 10 +- .../AddPanelWidget/AddPanelWidget.test.tsx | 5 +- .../components/DashNav/DashNav.test.tsx | 4 +- .../DashboardPrompt/DashboardPrompt.test.tsx | 71 ++++---- .../DashboardPrompt/DashboardPrompt.tsx | 14 +- .../AnnotationsSettings.test.tsx | 4 +- .../DashboardSettings.test.tsx | 4 +- .../GeneralSettings.test.tsx | 7 +- .../DashboardSettings/LinksSettings.test.tsx | 5 +- .../VersionsSettings.test.tsx | 8 +- .../PanelEditor/OptionsPaneOptions.test.tsx | 5 +- .../PanelEditor/PanelEditorTableView.test.tsx | 12 +- .../PanelEditor/state/actions.test.ts | 17 +- .../SaveDashboardDrawer.test.tsx | 3 +- .../forms/SaveDashboardAsForm.tsx | 1 - .../forms/SaveDashboardForm.test.tsx | 5 +- .../components/ShareModal/ShareEmbed.test.tsx | 11 +- .../components/ShareModal/ShareLink.test.tsx | 14 +- .../SharePublicDashboard.test.tsx | 8 +- .../VersionHistory/HistorySrv.test.ts | 5 +- .../dashgrid/PanelHeader/PanelHeader.test.tsx | 7 +- .../dashboard/state/DashboardMigrator.test.ts | 78 +++++++++ .../state/DashboardModel.refresh.test.ts | 4 +- .../dashboard/state/DashboardModel.test.ts | 148 +++++++++-------- .../dashboard/state/DashboardModel.ts | 15 +- .../state/__fixtures__/dashboardFixtures.ts | 68 ++++++++ .../features/dashboard/state/reducers.test.ts | 6 +- .../app/features/dashboard/state/reducers.ts | 8 +- .../dashboard/utils/getPanelMenu.test.ts | 9 +- .../dashboard/utils/panelMerge.test.ts | 20 +-- .../AddToDashboard/addToDashboard.test.ts | 2 + .../explore/AddToDashboard/index.test.tsx | 5 +- .../app/features/library-panels/state/api.ts | 2 + .../query/state/PanelQueryRunner.test.ts | 4 +- .../query/state/queryAnalytics.test.ts | 4 +- .../features/query/state/runRequest.test.ts | 4 +- .../interpolation/sceneInterpolator.ts | 2 +- .../state/onTimeRangeUpdated.test.ts | 3 +- .../state/templateVarsChangedInUrl.test.ts | 5 +- .../dashboard/DashboardQueryEditor.test.tsx | 19 ++- .../plugins/panel/graph/specs/graph.test.ts | 4 +- public/app/types/dashboard.ts | 8 +- 53 files changed, 879 insertions(+), 334 deletions(-) create mode 100644 public/app/features/dashboard/state/__fixtures__/dashboardFixtures.ts diff --git a/.betterer.results b/.betterer.results index 96a17907fd6..5378bdcd3e1 100644 --- a/.betterer.results +++ b/.betterer.results @@ -991,6 +991,11 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "3"], [0, 0, 0, "Do not use any type assertions.", "4"] ], + "packages/grafana-schema/src/veneer/dashboard.types.ts:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"], + [0, 0, 0, "Do not use any type assertions.", "2"] + ], "packages/grafana-toolkit/src/cli/tasks/component.create.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -3110,17 +3115,18 @@ exports[`better eslint`] = { ], "public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.test.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"] + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], + [0, 0, 0, "Do not use any type assertions.", "3"], [0, 0, 0, "Do not use any type assertions.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"] + [0, 0, 0, "Do not use any type assertions.", "5"], + [0, 0, 0, "Unexpected any. Specify a different type.", "6"], + [0, 0, 0, "Do not use any type assertions.", "7"], + [0, 0, 0, "Unexpected any. Specify a different type.", "8"] ], "public/app/features/dashboard/components/DashboardRow/DashboardRow.test.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -3469,15 +3475,14 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "26"], [0, 0, 0, "Unexpected any. Specify a different type.", "27"], [0, 0, 0, "Unexpected any. Specify a different type.", "28"], - [0, 0, 0, "Unexpected any. Specify a different type.", "29"], - [0, 0, 0, "Do not use any type assertions.", "30"], + [0, 0, 0, "Do not use any type assertions.", "29"], + [0, 0, 0, "Unexpected any. Specify a different type.", "30"], [0, 0, 0, "Unexpected any. Specify a different type.", "31"], [0, 0, 0, "Unexpected any. Specify a different type.", "32"], [0, 0, 0, "Unexpected any. Specify a different type.", "33"], [0, 0, 0, "Unexpected any. Specify a different type.", "34"], [0, 0, 0, "Unexpected any. Specify a different type.", "35"], - [0, 0, 0, "Unexpected any. Specify a different type.", "36"], - [0, 0, 0, "Unexpected any. Specify a different type.", "37"] + [0, 0, 0, "Unexpected any. Specify a different type.", "36"] ], "public/app/features/dashboard/state/PanelModel.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -3560,7 +3565,8 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/features/dashboard/state/reducers.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "public/app/features/dashboard/utils/getPanelMenu.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], diff --git a/devenv/dev-dashboards/alerting/testdata_alerts.json b/devenv/dev-dashboards/alerting/testdata_alerts.json index 61f4c763eab..e774554db20 100644 --- a/devenv/dev-dashboards/alerting/testdata_alerts.json +++ b/devenv/dev-dashboards/alerting/testdata_alerts.json @@ -90,13 +90,7 @@ "id": 4, "isNew": true, "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false + "show": true }, "lines": true, "linewidth": 2, @@ -233,13 +227,7 @@ "id": 7, "isNew": true, "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false + "show": true }, "lines": true, "linewidth": 2, @@ -399,13 +387,7 @@ "id": 6, "isNew": true, "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false + "show": true }, "lines": true, "linewidth": 2, @@ -537,13 +519,7 @@ "id": 3, "isNew": true, "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false + "show": true }, "lines": true, "linewidth": 2, @@ -679,13 +655,7 @@ "id": 5, "isNew": true, "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false + "show": true }, "lines": true, "linewidth": 2, diff --git a/kinds/structured/dashboard/dashboard_kind.cue b/kinds/structured/dashboard/dashboard_kind.cue index c0e9ddc6927..fa1fee27c0d 100644 --- a/kinds/structured/dashboard/dashboard_kind.cue +++ b/kinds/structured/dashboard/dashboard_kind.cue @@ -21,6 +21,9 @@ lineage: seqs: [ // Description of dashboard. description?: string + // Version of the current dashboard data + revision: int64 | *-1 @grafanamaturity(NeedsExpertReview) + gnetId?: string @grafanamaturity(NeedsExpertReview) // Tags associated with dashboard. tags?: [...string] @grafanamaturity(NeedsExpertReview) @@ -69,15 +72,17 @@ lineage: seqs: [ panels?: [...(#Panel | #RowPanel | #GraphPanel | #HeatmapPanel)] @grafanamaturity(NeedsExpertReview) // TODO docs templating?: { - list: [...#VariableModel] @grafanamaturity(NeedsExpertReview) + list?: [...#VariableModel] @grafanamaturity(NeedsExpertReview) } // TODO docs annotations?: { - list: [...#AnnotationQuery] @grafanamaturity(NeedsExpertReview) + list?: [...#AnnotationQuery] @grafanamaturity(NeedsExpertReview) } // TODO docs links?: [...#DashboardLink] @grafanamaturity(NeedsExpertReview) + snapshot?: #Snapshot @grafanamaturity(NeedsExpertReview) + /////////////////////////////////////// // Definitions (referenced above) are declared below @@ -119,20 +124,42 @@ lineage: seqs: [ // TODO what about what's in public/app/features/types.ts? // TODO there appear to be a lot of different kinds of [template] vars here? if so need a disjunction #VariableModel: { + id: string | *"00000000-0000-0000-0000-000000000000" type: #VariableType name: string label?: string + rootStateKey?: string + global: bool | *false + hide: #VariableHide + skipUrlSync: bool | *false + index: int32 | *-1 + state: #LoadingState + error?: {...} + description?: string + // TODO: Move this into a separated QueryVariableModel type + query?: string | {...} + datasource?: #DataSourceRef ... - } @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) + } @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview) + // TODO: There is a bug generating the names, they are always title case + #VariableHide: 0 | 1 | 2 @cuetsy(kind="enum",memberNames="dontHide|hideLabel|hideVariable") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview) + #LoadingState: "NotStarted" | "Loading" | "Streaming" | "Done" | "Error" @cuetsy(kind="enum") @grafanamaturity(NeedsExpertReview) + // Ref to a DataSource instance + #DataSourceRef: { + // The plugin type-id + type?: string @grafanamaturity(NeedsExpertReview) + // Specific datasource instance + uid?: string @grafanamaturity(NeedsExpertReview) + } @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) // FROM public/app/features/dashboard/state/DashboardModels.ts - ish // TODO docs #DashboardLink: { title: string @grafanamaturity(NeedsExpertReview) type: #DashboardLinkType @grafanamaturity(NeedsExpertReview) - icon?: string @grafanamaturity(NeedsExpertReview) - tooltip?: string @grafanamaturity(NeedsExpertReview) - url?: string @grafanamaturity(NeedsExpertReview) + icon: string @grafanamaturity(NeedsExpertReview) + tooltip: string @grafanamaturity(NeedsExpertReview) + url: string @grafanamaturity(NeedsExpertReview) tags: [...string] @grafanamaturity(NeedsExpertReview) asDropdown: bool | *false @grafanamaturity(NeedsExpertReview) targetBlank: bool | *false @grafanamaturity(NeedsExpertReview) @@ -273,6 +300,43 @@ lineage: seqs: [ // type directly to achieve the same effect. #Target: {...} @grafanamaturity(NeedsExpertReview) + // TODO docs + #Snapshot: { + + // TODO docs + created: string @grafanamaturity(NeedsExpertReview) + + // TODO docs + expires: string @grafanamaturity(NeedsExpertReview) + + // TODO docs + external: bool @grafanamaturity(NeedsExpertReview) + + // TODO docs + externalUrl: string @grafanamaturity(NeedsExpertReview) + + // TODO docs + id: uint32 @grafanamaturity(NeedsExpertReview) + + // TODO docs + key: string @grafanamaturity(NeedsExpertReview) + + // TODO docs + name: string @grafanamaturity(NeedsExpertReview) + + // TODO docs + orgId: uint32 @grafanamaturity(NeedsExpertReview) + + // TODO docs + updated: string @grafanamaturity(NeedsExpertReview) + + // TODO docs + url?: string @grafanamaturity(NeedsExpertReview) + + // TODO docs + userId: uint32 @grafanamaturity(NeedsExpertReview) + } @grafanamaturity(NeedsExpertReview) + // Dashboard panels. Panels are canonically defined inline // because they share a version timeline with the dashboard // schema; they do not evolve independently. @@ -313,7 +377,10 @@ lineage: seqs: [ repeat?: string @grafanamaturity(NeedsExpertReview) // Direction to repeat in if 'repeat' is set. // "h" for horizontal, "v" for vertical. + // TODO this is probably optional repeatDirection: *"h" | "v" @grafanamaturity(NeedsExpertReview) + // Id of the repeating panel. + repeatPanelId?: int64 @grafanamaturity(NeedsExpertReview) // TODO docs maxDataPoints?: number @grafanamaturity(NeedsExpertReview) @@ -441,12 +508,18 @@ lineage: seqs: [ // Support for legacy graph and heatmap panels. #GraphPanel: { type: "graph" @grafanamaturity(NeedsExpertReview) + // @deprecated this is part of deprecated graph panel + legend?: { + show: bool | *true + sort?: string + sortDesc?: bool + } ... - } @grafanamaturity(NeedsExpertReview) + } @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) #HeatmapPanel: { type: "heatmap" @grafanamaturity(NeedsExpertReview) ... - } @grafanamaturity(NeedsExpertReview) + } @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) }, ] }, diff --git a/packages/grafana-data/src/types/templateVars.ts b/packages/grafana-data/src/types/templateVars.ts index f723d20a341..0395556f8ef 100644 --- a/packages/grafana-data/src/types/templateVars.ts +++ b/packages/grafana-data/src/types/templateVars.ts @@ -144,10 +144,11 @@ export interface SystemVariable string }> exten current: { value: TProps }; } -export interface BaseVariableModel extends VariableModel { +export interface BaseVariableModel { name: string; label?: string; id: string; + type: VariableType; rootStateKey: string | null; global: boolean; hide: VariableHide; diff --git a/packages/grafana-schema/src/index.gen.ts b/packages/grafana-schema/src/index.gen.ts index f875870120a..11a120b88e5 100644 --- a/packages/grafana-schema/src/index.gen.ts +++ b/packages/grafana-schema/src/index.gen.ts @@ -11,38 +11,41 @@ export type { AnnotationTarget, AnnotationQuery, - VariableModel, + DataSourceRef, DashboardLink, DashboardLinkType, VariableType, - FieldColorModeId, FieldColorSeriesByMode, FieldColor, GridPos, Threshold, - ThresholdsMode, ThresholdsConfig, ValueMapping, - MappingType, ValueMap, RangeMap, RegexMap, SpecialValueMap, - SpecialValueMatch, ValueMappingResult, Transformation, - DashboardCursorSync, MatcherConfig, - RowPanel + RowPanel, + GraphPanel, + HeatmapPanel } from './raw/dashboard/x/dashboard_types.gen'; -// Raw generated default consts from dashboard kind. +// Raw generated enums and default consts from dashboard kind. export { defaultAnnotationTarget, defaultAnnotationQuery, + LoadingState, defaultDashboardLink, + FieldColorModeId, defaultGridPos, + ThresholdsMode, defaultThresholdsConfig, + MappingType, + SpecialValueMatch, + DashboardCursorSync, defaultDashboardCursorSync, defaultMatcherConfig, defaultRowPanel @@ -59,6 +62,7 @@ export { // TODO generate code such that tsc enforces type compatibility between raw and veneer decls export type { Dashboard, + VariableModel, Panel, FieldConfigSource, FieldConfig @@ -75,6 +79,8 @@ export type { // TODO generate code such that tsc enforces type compatibility between raw and veneer decls export { defaultDashboard, + defaultVariableModel, + VariableHide, defaultPanel, defaultFieldConfigSource, defaultFieldConfig @@ -86,11 +92,11 @@ export type { PlaylistItem } from './raw/playlist/x/playlist_types.gen'; -// Raw generated default consts from playlist kind. +// Raw generated enums and default consts from playlist kind. export { defaultPlaylist } from './raw/playlist/x/playlist_types.gen'; // Raw generated types from Team kind. -export type { - Team, - Permission -} from './raw/team/x/team_types.gen'; +export type { Team } from './raw/team/x/team_types.gen'; + +// Raw generated enums and default consts from team kind. +export { Permission } from './raw/team/x/team_types.gen'; diff --git a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts index f89a0ae4abf..3e95cbf1539 100644 --- a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts +++ b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts @@ -75,26 +75,78 @@ export const defaultAnnotationQuery: Partial = { * TODO there appear to be a lot of different kinds of [template] vars here? if so need a disjunction */ export interface VariableModel { + datasource?: DataSourceRef; + description?: string; + error?: Record; + global: boolean; + hide: VariableHide; + id: string; + index: number; label?: string; name: string; + /** + * TODO: Move this into a separated QueryVariableModel type + */ + query?: (string | Record); + rootStateKey?: string; + skipUrlSync: boolean; + state: LoadingState; type: VariableType; } +export const defaultVariableModel: Partial = { + global: false, + id: '00000000-0000-0000-0000-000000000000', + index: -1, + skipUrlSync: false, +}; + +/** + * TODO: There is a bug generating the names, they are always title case + */ +export enum VariableHide { + DontHide = 0, + HideLabel = 1, + HideVariable = 2, +} + +export enum LoadingState { + Done = 'Done', + Error = 'Error', + Loading = 'Loading', + NotStarted = 'NotStarted', + Streaming = 'Streaming', +} + +/** + * Ref to a DataSource instance + */ +export interface DataSourceRef { + /** + * The plugin type-id + */ + type?: string; + /** + * Specific datasource instance + */ + uid?: string; +} + /** * FROM public/app/features/dashboard/state/DashboardModels.ts - ish * TODO docs */ export interface DashboardLink { asDropdown: boolean; - icon?: string; + icon: string; includeVars: boolean; keepTime: boolean; tags: Array; targetBlank: boolean; title: string; - tooltip?: string; + tooltip: string; type: DashboardLinkType; - url?: string; + url: string; } export const defaultDashboardLink: Partial = { @@ -380,8 +432,13 @@ export interface Panel { /** * Direction to repeat in if 'repeat' is set. * "h" for horizontal, "v" for vertical. + * TODO this is probably optional */ repeatDirection: ('h' | 'v'); + /** + * Id of the repeating panel. + */ + repeatPanelId?: number; /** * TODO docs */ @@ -544,11 +601,7 @@ export interface RowPanel { }; gridPos?: GridPos; id: number; - panels: Array<(Panel | { - type: 'graph'; - } | { - type: 'heatmap'; - })>; + panels: Array<(Panel | GraphPanel | HeatmapPanel)>; /** * Name of template variable to repeat for. */ @@ -562,12 +615,31 @@ export const defaultRowPanel: Partial = { panels: [], }; +/** + * Support for legacy graph and heatmap panels. + */ +export interface GraphPanel { + /** + * @deprecated this is part of deprecated graph panel + */ + legend?: { + show: boolean; + sort?: string; + sortDesc?: boolean; + }; + type: 'graph'; +} + +export interface HeatmapPanel { + type: 'heatmap'; +} + export interface Dashboard { /** * TODO docs */ annotations?: { - list: Array; + list?: Array; }; /** * Description of dashboard. @@ -596,21 +668,67 @@ export interface Dashboard { * TODO docs */ liveNow?: boolean; - panels?: Array<(Panel | RowPanel | { - type: 'graph'; - } | { - type: 'heatmap'; - })>; + panels?: Array<(Panel | RowPanel | GraphPanel | HeatmapPanel)>; /** * TODO docs */ refresh?: (string | false); + /** + * Version of the current dashboard data + */ + revision: number; /** * Version of the JSON schema, incremented each time a Grafana update brings * changes to said schema. * TODO this is the existing schema numbering system. It will be replaced by Thema's themaVersion */ schemaVersion: number; + snapshot?: { + /** + * TODO docs + */ + created: string; + /** + * TODO docs + */ + expires: string; + /** + * TODO docs + */ + external: boolean; + /** + * TODO docs + */ + externalUrl: string; + /** + * TODO docs + */ + id: number; + /** + * TODO docs + */ + key: string; + /** + * TODO docs + */ + name: string; + /** + * TODO docs + */ + orgId: number; + /** + * TODO docs + */ + updated: string; + /** + * TODO docs + */ + url?: string; + /** + * TODO docs + */ + userId: number; + }; /** * Theme of dashboard. */ @@ -623,7 +741,7 @@ export interface Dashboard { * TODO docs */ templating?: { - list: Array; + list?: Array; }; /** * Time range for dashboard, e.g. last 6 hours, last 7 days, etc @@ -685,6 +803,7 @@ export const defaultDashboard: Partial = { graphTooltip: DashboardCursorSync.Off, links: [], panels: [], + revision: -1, schemaVersion: 36, style: 'dark', tags: [], diff --git a/packages/grafana-schema/src/veneer/dashboard.types.ts b/packages/grafana-schema/src/veneer/dashboard.types.ts index 013d76db039..b2e7b53433e 100644 --- a/packages/grafana-schema/src/veneer/dashboard.types.ts +++ b/packages/grafana-schema/src/veneer/dashboard.types.ts @@ -1,23 +1,33 @@ import * as raw from '../raw/dashboard/x/dashboard_types.gen'; -export interface Dashboard extends raw.Dashboard { - panels?: Array< - | Panel - | raw.RowPanel - | { - type: 'graph'; - } - | { - type: 'heatmap'; - } - >; -} - export interface Panel, TCustomFieldConfig = Record> extends raw.Panel { fieldConfig: FieldConfigSource; } +export enum VariableHide { + dontHide, + hideLabel, + hideVariable, +} + +export interface VariableModel + extends Omit { + // Overrides nullable properties because CUE doesn't support null values + rootStateKey: string | null; + error: any | null; + description: string | null; + hide: VariableHide; + datasource: raw.DataSourceRef | null; +} + +export interface Dashboard extends Omit { + panels?: Array; + templating?: { + list?: VariableModel[]; + }; +} + export interface FieldConfig> extends raw.FieldConfig { custom?: TOptions & Record; } @@ -26,7 +36,16 @@ export interface FieldConfigSource> extends r defaults: FieldConfig; } -export const defaultDashboard: Partial = raw.defaultDashboard; +export const defaultDashboard = raw.defaultDashboard as Dashboard; +export const defaultVariableModel = { + ...raw.defaultVariableModel, + rootStateKey: null, + error: null, + description: null, + hide: VariableHide.dontHide, + state: raw.LoadingState.NotStarted, + datasource: null, +} as VariableModel; export const defaultPanel: Partial = raw.defaultPanel; export const defaultFieldConfig: Partial = raw.defaultFieldConfig; export const defaultFieldConfigSource: Partial = raw.defaultFieldConfigSource; diff --git a/pkg/codegen/jenny_tsveneerindex.go b/pkg/codegen/jenny_tsveneerindex.go index cfe70117ca5..407a273494b 100644 --- a/pkg/codegen/jenny_tsveneerindex.go +++ b/pkg/codegen/jenny_tsveneerindex.go @@ -79,7 +79,7 @@ func (gen *genTSVeneerIndex) extractTSIndexVeneerElements(decl *DeclForGen, tf * sels := p.Selectors() switch len(sels) { case 0: - name = strings.Title(lin.Name()) + name = comm.Name fallthrough case 1: // Only deal with subpaths that are definitions, for now @@ -110,12 +110,24 @@ func (gen *genTSVeneerIndex) extractTSIndexVeneerElements(decl *DeclForGen, tf * has = has || tgt.target == "type" } if has { - custom = append(custom, *pair.T) + // enums can't use 'export type' + if pair.isEnum { + customD = append(customD, *pair.T) + } else { + custom = append(custom, *pair.T) + } + if pair.D != nil { customD = append(customD, *pair.D) } } else { - raw = append(raw, *pair.T) + // enums can't use 'export type' + if pair.isEnum { + rawD = append(rawD, *pair.T) + } else { + raw = append(raw, *pair.T) + } + if pair.D != nil { rawD = append(rawD, *pair.D) } @@ -146,7 +158,7 @@ func (gen *genTSVeneerIndex) extractTSIndexVeneerElements(decl *DeclForGen, tf * } if len(rawD) > 0 { ret = append(ret, ast.ExportSet{ - CommentList: []ast.Comment{ts.CommentFromString(fmt.Sprintf("Raw generated default consts from %s kind.", lin.Name()), 80, false)}, + CommentList: []ast.Comment{ts.CommentFromString(fmt.Sprintf("Raw generated enums and default consts from %s kind.", lin.Name()), 80, false)}, TypeOnly: false, Exports: rawD, From: ast.Str{Value: fmt.Sprintf("./raw/%s/%s/%s_types.gen", comm.MachineName, vpath, comm.MachineName)}, @@ -187,7 +199,8 @@ func (gen *genTSVeneerIndex) extractTSIndexVeneerElements(decl *DeclForGen, tf * } type declPair struct { - T, D *ast.Ident + T, D *ast.Ident + isEnum bool } type tsVeneerAttr struct { @@ -206,6 +219,7 @@ func findDeclNode(name string, tf *ast.File) declPair { case ast.TypeDecl: if x.Name.Name == name { p.T = &x.Name + _, p.isEnum = x.Type.(ast.EnumType) } case ast.VarDecl: if x.Names.Idents[0].Name == "default"+name { diff --git a/pkg/kinds/dashboard/dashboard_types_gen.go b/pkg/kinds/dashboard/dashboard_types_gen.go index c8faba1a7a5..1b1a1dde33f 100644 --- a/pkg/kinds/dashboard/dashboard_types_gen.go +++ b/pkg/kinds/dashboard/dashboard_types_gen.go @@ -83,6 +83,19 @@ const ( HeatmapPanelTypeHeatmap HeatmapPanelType = "heatmap" ) +// Defines values for LoadingState. +const ( + LoadingStateDone LoadingState = "Done" + + LoadingStateError LoadingState = "Error" + + LoadingStateLoading LoadingState = "Loading" + + LoadingStateNotStarted LoadingState = "NotStarted" + + LoadingStateStreaming LoadingState = "Streaming" +) + // Defines values for MappingType. const ( MappingTypeRange MappingType = "range" @@ -162,6 +175,37 @@ const ( ValueMapTypeValue ValueMapType = "value" ) +// Defines values for VariableHide. +const ( + VariableHideN0 VariableHide = 0 + + VariableHideN1 VariableHide = 1 + + VariableHideN2 VariableHide = 2 +) + +// Defines values for VariableModelHide. +const ( + VariableModelHideN0 VariableModelHide = 0 + + VariableModelHideN1 VariableModelHide = 1 + + VariableModelHideN2 VariableModelHide = 2 +) + +// Defines values for VariableModelState. +const ( + VariableModelStateDone VariableModelState = "Done" + + VariableModelStateError VariableModelState = "Error" + + VariableModelStateLoading VariableModelState = "Loading" + + VariableModelStateNotStarted VariableModelState = "NotStarted" + + VariableModelStateStreaming VariableModelState = "Streaming" +) + // Defines values for VariableModelType. const ( VariableModelTypeAdhoc VariableModelType = "adhoc" @@ -204,7 +248,7 @@ const ( type Dashboard struct { Annotations *struct { // TODO docs - List []AnnotationQuery `json:"list"` + List *[]AnnotationQuery `json:"list,omitempty"` } `json:"annotations,omitempty"` // Description of dashboard. @@ -232,11 +276,17 @@ type Dashboard struct { // TODO docs Refresh *interface{} `json:"refresh,omitempty"` + // Version of the current dashboard data + Revision int `json:"revision"` + // Version of the JSON schema, incremented each time a Grafana update brings // changes to said schema. // TODO this is the existing schema numbering system. It will be replaced by Thema's themaVersion SchemaVersion int `json:"schemaVersion"` + // TODO docs + Snapshot *Snapshot `json:"snapshot,omitempty"` + // Theme of dashboard. Style Style `json:"style"` @@ -244,7 +294,7 @@ type Dashboard struct { Tags *[]string `json:"tags,omitempty"` Templating *struct { // TODO docs - List []VariableModel `json:"list"` + List *[]VariableModel `json:"list,omitempty"` } `json:"templating,omitempty"` // Time range for dashboard, e.g. last 6 hours, last 7 days, etc @@ -346,20 +396,29 @@ type DashboardCursorSync int // TODO docs type DashboardLink struct { AsDropdown bool `json:"asDropdown"` - Icon *string `json:"icon,omitempty"` + Icon string `json:"icon"` IncludeVars bool `json:"includeVars"` KeepTime bool `json:"keepTime"` Tags []string `json:"tags"` TargetBlank bool `json:"targetBlank"` Title string `json:"title"` - Tooltip *string `json:"tooltip,omitempty"` + Tooltip string `json:"tooltip"` Type DashboardLinkType `json:"type"` - Url *string `json:"url,omitempty"` + Url string `json:"url"` } // DashboardLinkType defines model for DashboardLink.Type. type DashboardLinkType string +// Ref to a DataSource instance +type DataSourceRef struct { + // The plugin type-id + Type *string `json:"type,omitempty"` + + // Specific datasource instance + Uid *string `json:"uid,omitempty"` +} + // DynamicConfigValue defines model for dashboard.DynamicConfigValue. type DynamicConfigValue struct { Id string `json:"id"` @@ -498,13 +557,18 @@ type FieldConfigSource struct { } `json:"overrides"` } -// GraphPanel defines model for dashboard.GraphPanel. +// Support for legacy graph and heatmap panels. type GraphPanel struct { - // Support for legacy graph and heatmap panels. + // @deprecated this is part of deprecated graph panel + Legend *struct { + Show bool `json:"show"` + Sort *string `json:"sort,omitempty"` + SortDesc *bool `json:"sortDesc,omitempty"` + } `json:"legend,omitempty"` Type GraphPanelType `json:"type"` } -// Support for legacy graph and heatmap panels. +// GraphPanelType defines model for GraphPanel.Type. type GraphPanelType string // GridPos defines model for dashboard.GridPos. @@ -533,6 +597,9 @@ type HeatmapPanel struct { // HeatmapPanelType defines model for HeatmapPanel.Type. type HeatmapPanelType string +// LoadingState defines model for dashboard.LoadingState. +type LoadingState string + // TODO docs type MappingType string @@ -643,8 +710,12 @@ type Panel struct { // Direction to repeat in if 'repeat' is set. // "h" for horizontal, "v" for vertical. + // TODO this is probably optional RepeatDirection PanelRepeatDirection `json:"repeatDirection"` + // Id of the repeating panel. + RepeatPanelId *int64 `json:"repeatPanelId,omitempty"` + // TODO docs Tags *[]string `json:"tags,omitempty"` @@ -681,6 +752,7 @@ type Panel struct { // Direction to repeat in if 'repeat' is set. // "h" for horizontal, "v" for vertical. +// TODO this is probably optional type PanelRepeatDirection string // TODO docs @@ -741,6 +813,42 @@ type RowPanel struct { // RowPanelType defines model for RowPanel.Type. type RowPanelType string +// TODO docs +type Snapshot struct { + // TODO docs + Created string `json:"created"` + + // TODO docs + Expires string `json:"expires"` + + // TODO docs + External bool `json:"external"` + + // TODO docs + ExternalUrl string `json:"externalUrl"` + + // TODO docs + Id int `json:"id"` + + // TODO docs + Key string `json:"key"` + + // TODO docs + Name string `json:"name"` + + // TODO docs + OrgId int `json:"orgId"` + + // TODO docs + Updated string `json:"updated"` + + // TODO docs + Url *string `json:"url,omitempty"` + + // TODO docs + UserId int `json:"userId"` +} + // TODO docs type SpecialValueMap struct { Options struct { @@ -842,16 +950,39 @@ type ValueMappingResult struct { Text *string `json:"text,omitempty"` } +// TODO: There is a bug generating the names, they are always title case +type VariableHide int + // FROM: packages/grafana-data/src/types/templateVars.ts // TODO docs // TODO what about what's in public/app/features/types.ts? // TODO there appear to be a lot of different kinds of [template] vars here? if so need a disjunction type VariableModel struct { - Label *string `json:"label,omitempty"` - Name string `json:"name"` - Type VariableModelType `json:"type"` + // Ref to a DataSource instance + Datasource *DataSourceRef `json:"datasource,omitempty"` + Description *string `json:"description,omitempty"` + Error *map[string]interface{} `json:"error,omitempty"` + Global bool `json:"global"` + Hide VariableModelHide `json:"hide"` + Id string `json:"id"` + Index int `json:"index"` + Label *string `json:"label,omitempty"` + Name string `json:"name"` + + // TODO: Move this into a separated QueryVariableModel type + Query *interface{} `json:"query,omitempty"` + RootStateKey *string `json:"rootStateKey,omitempty"` + SkipUrlSync bool `json:"skipUrlSync"` + State VariableModelState `json:"state"` + Type VariableModelType `json:"type"` } +// VariableModelHide defines model for VariableModel.Hide. +type VariableModelHide int + +// VariableModelState defines model for VariableModel.State. +type VariableModelState string + // VariableModelType defines model for VariableModel.Type. type VariableModelType string diff --git a/public/app/features/alerting/TestRuleResult.test.tsx b/public/app/features/alerting/TestRuleResult.test.tsx index 17cadde5f53..d80398c0564 100644 --- a/public/app/features/alerting/TestRuleResult.test.tsx +++ b/public/app/features/alerting/TestRuleResult.test.tsx @@ -1,7 +1,8 @@ import { render } from '@testing-library/react'; import React from 'react'; -import { DashboardModel, PanelModel } from '../dashboard/state'; +import { PanelModel } from '../dashboard/state'; +import { createDashboardModelFixture, createPanelJSONFixture } from '../dashboard/state/__fixtures__/dashboardFixtures'; import { TestRuleResult, Props } from './TestRuleResult'; @@ -18,7 +19,9 @@ jest.mock('@grafana/runtime', () => { const props: Props = { panel: new PanelModel({ id: 1 }), - dashboard: new DashboardModel({ panels: [{ id: 1 }] }), + dashboard: createDashboardModelFixture({ + panels: [createPanelJSONFixture({ id: 1 })], + }), }; describe('TestRuleResult', () => { diff --git a/public/app/features/alerting/unified/components/panel-alerts-tab/NewRuleFromPanelButton.test.tsx b/public/app/features/alerting/unified/components/panel-alerts-tab/NewRuleFromPanelButton.test.tsx index 9950ba64363..dbe2944c696 100644 --- a/public/app/features/alerting/unified/components/panel-alerts-tab/NewRuleFromPanelButton.test.tsx +++ b/public/app/features/alerting/unified/components/panel-alerts-tab/NewRuleFromPanelButton.test.tsx @@ -3,7 +3,8 @@ import userEvent from '@testing-library/user-event'; import React from 'react'; import { logInfo } from '@grafana/runtime'; -import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; +import { PanelModel } from 'app/features/dashboard/state'; +import { createDashboardModelFixture } from 'app/features/dashboard/state/__fixtures__/dashboardFixtures'; import { LogMessages } from '../../Analytics'; @@ -40,7 +41,7 @@ describe('Analytics', () => { const panel = new PanelModel({ id: 123, }); - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ id: 1, }); render(); diff --git a/public/app/features/alerting/unified/components/rule-editor/AnnotationsField.test.tsx b/public/app/features/alerting/unified/components/rule-editor/AnnotationsField.test.tsx index 8a47dbd9992..a5645d048cf 100644 --- a/public/app/features/alerting/unified/components/rule-editor/AnnotationsField.test.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/AnnotationsField.test.tsx @@ -8,6 +8,7 @@ import { Provider } from 'react-redux'; import { byRole, byTestId } from 'testing-library-selector'; import { setBackendSrv } from '@grafana/runtime'; +import { defaultDashboard } from '@grafana/schema'; import { backendSrv } from 'app/core/services/backend_srv'; import { DashboardDTO } from '../../../../../types'; @@ -259,15 +260,12 @@ function mockDashboardSearchItem(searchItem: Partial) { }; } -function mockDashboardDto(dashboard: Partial) { +function mockDashboardDto(dashboard: Partial): DashboardDTO { return { dashboard: { - title: '', - uid: '', - templating: { list: [] }, - panels: [], + ...defaultDashboard, ...dashboard, - }, + } as DashboardDTO['dashboard'], meta: {}, }; } diff --git a/public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.test.tsx b/public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.test.tsx index 9811e10a966..d0f0690cd4a 100644 --- a/public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.test.tsx +++ b/public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.test.tsx @@ -1,13 +1,14 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; -import { DashboardModel, PanelModel } from '../../state'; +import { PanelModel } from '../../state'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { AddPanelWidgetUnconnected as AddPanelWidget, Props } from './AddPanelWidget'; const getTestContext = (propOverrides?: object) => { const props: Props = { - dashboard: new DashboardModel({}), + dashboard: createDashboardModelFixture(), panel: new PanelModel({}), addPanel: jest.fn() as any, }; diff --git a/public/app/features/dashboard/components/DashNav/DashNav.test.tsx b/public/app/features/dashboard/components/DashNav/DashNav.test.tsx index 14e966d340a..2bb811d0582 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.test.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.test.tsx @@ -10,13 +10,13 @@ import { getGrafanaContextMock } from '../../../../../test/mocks/getGrafanaConte import { setStarred } from '../../../../core/reducers/navBarTree'; import { configureStore } from '../../../../store/configureStore'; import { updateTimeZoneForSession } from '../../../profile/state/reducers'; -import { DashboardModel } from '../../state'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { DashNav } from './DashNav'; describe('Public dashboard title tag', () => { it('will be rendered when publicDashboardEnabled set to true in dashboard meta', async () => { - let dashboard = new DashboardModel({}, { publicDashboardEnabled: false }); + let dashboard = createDashboardModelFixture({}, { publicDashboardEnabled: false }); const store = configureStore(); const context = getGrafanaContextMock(); diff --git a/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.test.tsx b/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.test.tsx index 34c97325d9b..cd5974264ef 100644 --- a/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.test.tsx +++ b/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.test.tsx @@ -1,21 +1,22 @@ import { getPanelPlugin } from 'app/features/plugins/__mocks__/pluginMocks'; import { setContextSrv } from '../../../../core/services/context_srv'; -import { DashboardModel } from '../../state/DashboardModel'; import { PanelModel } from '../../state/PanelModel'; +import { createDashboardModelFixture, createPanelJSONFixture } from '../../state/__fixtures__/dashboardFixtures'; import { hasChanges, ignoreChanges } from './DashboardPrompt'; -function getDefaultDashboardModel(): DashboardModel { - return new DashboardModel({ +function getDefaultDashboardModel() { + return createDashboardModelFixture({ refresh: false, panels: [ - { + createPanelJSONFixture({ id: 1, type: 'graph', gridPos: { x: 0, y: 0, w: 24, h: 6 }, - legend: { sortDesc: false }, - }, + legend: { show: true, sortDesc: false }, // TODO legend is marked as a non-persisted field + }), + { id: 2, type: 'row', @@ -26,7 +27,7 @@ function getDefaultDashboardModel(): DashboardModel { { id: 4, type: 'graph', gridPos: { x: 12, y: 6, w: 12, h: 2 } }, ], }, - { id: 5, type: 'row', gridPos: { x: 0, y: 6, w: 1, h: 1 } }, + { id: 5, type: 'row', gridPos: { x: 0, y: 6, w: 1, h: 1 }, collapsed: false, panels: [] }, ], }); } @@ -34,8 +35,8 @@ function getDefaultDashboardModel(): DashboardModel { function getTestContext() { const contextSrv: any = { isSignedIn: true, isEditor: true }; setContextSrv(contextSrv); - const dash: any = getDefaultDashboardModel(); - const original: any = dash.getSaveModelClone(); + const dash = getDefaultDashboardModel(); + const original = dash.getSaveModelClone(); return { dash, original, contextSrv }; } @@ -68,8 +69,8 @@ describe('DashboardPrompt', () => { it('Should ignore panel legend changes', () => { const { original, dash } = getTestContext(); - dash.panels[0].legend.sortDesc = true; - dash.panels[0].legend.sort = 'avg'; + dash.panels[0]!.legend!.sortDesc = true; + dash.panels[0]!.legend!.sort = 'avg'; expect(hasChanges(dash, original)).toBe(false); }); @@ -90,47 +91,45 @@ describe('DashboardPrompt', () => { describe('when called without current dashboard', () => { it('then it should return true', () => { const { original } = getTestContext(); - expect(ignoreChanges(null as unknown as DashboardModel, original)).toBe(true); - }); - }); - - describe('when called without meta in current dashboard', () => { - it('then it should return true', () => { - const { original, dash } = getTestContext(); - expect(ignoreChanges({ ...dash, meta: undefined }, original)).toBe(true); + expect(ignoreChanges(null, original)).toBe(true); }); }); describe('when called for a viewer without save permissions', () => { it('then it should return true', () => { - const { original, dash, contextSrv } = getTestContext(); + const { contextSrv } = getTestContext(); + const dash = createDashboardModelFixture({}, { canSave: false }); + const original = dash.getSaveModelClone(); contextSrv.isEditor = false; - expect(ignoreChanges({ ...dash, meta: { canSave: false } }, original)).toBe(true); + expect(ignoreChanges(dash, original)).toBe(true); }); }); describe('when called for a viewer with save permissions', () => { it('then it should return undefined', () => { - const { original, dash, contextSrv } = getTestContext(); + const { contextSrv } = getTestContext(); + const dash = createDashboardModelFixture({}, { canSave: true }); + const original = dash.getSaveModelClone(); contextSrv.isEditor = false; - expect(ignoreChanges({ ...dash, meta: { canSave: true } }, original)).toBe(undefined); + expect(ignoreChanges(dash, original)).toBe(undefined); }); }); describe('when called for an user that is not signed in', () => { it('then it should return true', () => { - const { original, dash, contextSrv } = getTestContext(); + const { contextSrv } = getTestContext(); + const dash = createDashboardModelFixture({}, { canSave: true }); + const original = dash.getSaveModelClone(); contextSrv.isSignedIn = false; - expect(ignoreChanges({ ...dash, meta: { canSave: true } }, original)).toBe(true); + expect(ignoreChanges(dash, original)).toBe(true); }); }); describe('when called with fromScript', () => { it('then it should return true', () => { - const { original, dash } = getTestContext(); - expect( - ignoreChanges({ ...dash, meta: { canSave: true, fromScript: true, fromFile: undefined } }, original) - ).toBe(true); + const dash = createDashboardModelFixture({}, { canSave: true, fromScript: true, fromFile: undefined }); + const original = dash.getSaveModelClone(); + expect(ignoreChanges(dash, original)).toBe(true); }); }); @@ -147,19 +146,17 @@ describe('DashboardPrompt', () => { describe('when called with fromFile', () => { it('then it should return true', () => { - const { original, dash } = getTestContext(); - expect( - ignoreChanges({ ...dash, meta: { canSave: true, fromScript: undefined, fromFile: true } }, original) - ).toBe(true); + const dash = createDashboardModelFixture({}, { canSave: true, fromScript: undefined, fromFile: true }); + const original = dash.getSaveModelClone(); + expect(ignoreChanges(dash, original)).toBe(true); }); }); describe('when called with canSave but without fromScript and fromFile', () => { it('then it should return false', () => { - const { original, dash } = getTestContext(); - expect( - ignoreChanges({ ...dash, meta: { canSave: true, fromScript: undefined, fromFile: undefined } }, original) - ).toBe(undefined); + const dash = createDashboardModelFixture({}, { canSave: true, fromScript: undefined, fromFile: undefined }); + const original = dash.getSaveModelClone(); + expect(ignoreChanges(dash, original)).toBe(undefined); }); }); }); diff --git a/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx b/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx index 0ae88841c4e..42133dd7976 100644 --- a/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx +++ b/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx @@ -4,6 +4,7 @@ import React, { useContext, useEffect, useState } from 'react'; import { Prompt } from 'react-router-dom'; import { locationService } from '@grafana/runtime'; +import { Dashboard } from '@grafana/schema'; import { ModalsContext } from '@grafana/ui'; import { appEvents } from 'app/core/app_events'; import { contextSrv } from 'app/core/services/context_srv'; @@ -140,7 +141,7 @@ function moveToBlockedLocationAfterReactStateUpdate(location?: H.Location | null /** * For some dashboards and users changes should be ignored * */ -export function ignoreChanges(current: DashboardModel, original: object | null) { +export function ignoreChanges(current: DashboardModel | null, original: object | null) { if (!original) { return true; } @@ -150,7 +151,7 @@ export function ignoreChanges(current: DashboardModel, original: object | null) return true; } - if (!current || !current.meta) { + if (!current) { return true; } @@ -165,7 +166,7 @@ export function ignoreChanges(current: DashboardModel, original: object | null) /** * Remove stuff that should not count in diff */ -function cleanDashboardFromIgnoredChanges(dashData: unknown) { +function cleanDashboardFromIgnoredChanges(dashData: Dashboard) { // need to new up the domain model class to get access to expand / collapse row logic const model = new DashboardModel(dashData); @@ -193,13 +194,14 @@ function cleanDashboardFromIgnoredChanges(dashData: unknown) { return dash; } +// TODO: Adapt original to be Dashboard type instead export function hasChanges(current: DashboardModel, original: unknown) { if (current.hasUnsavedChanges()) { return true; } - - const currentClean = cleanDashboardFromIgnoredChanges(current.getSaveModelClone()); - const originalClean = cleanDashboardFromIgnoredChanges(original); + // TODO: Make getSaveModelClone return Dashboard type instead + const currentClean = cleanDashboardFromIgnoredChanges(current.getSaveModelClone() as unknown as Dashboard); + const originalClean = cleanDashboardFromIgnoredChanges(original as Dashboard); const currentTimepicker = find((currentClean as any).nav, { type: 'timepicker' }); const originalTimepicker = find((originalClean as any).nav, { type: 'timepicker' }); diff --git a/public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx index 3d68416cfa7..349576f5374 100644 --- a/public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx @@ -13,6 +13,7 @@ import { mockDataSource, MockDataSourceSrv } from 'app/features/alerting/unified import { configureStore } from '../../../../store/configureStore'; import { DashboardModel } from '../../state/DashboardModel'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { AnnotationsSettings } from './AnnotationsSettings'; @@ -83,7 +84,7 @@ describe('AnnotationsSettings', () => { }); beforeEach(() => { - dashboard = new DashboardModel({ + dashboard = createDashboardModelFixture({ id: 74, version: 7, annotations: { @@ -96,6 +97,7 @@ describe('AnnotationsSettings', () => { iconColor: 'rgba(0, 211, 255, 1)', name: 'Annotations & Alerts', type: 'dashboard', + showIn: 1, }, ], }, diff --git a/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.test.tsx index 81558d8374f..2dfb2b0817e 100644 --- a/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.test.tsx @@ -9,7 +9,7 @@ import { BackendSrv, setBackendSrv } from '@grafana/runtime'; import { GrafanaContext } from 'app/core/context/GrafanaContext'; import { configureStore } from 'app/store/configureStore'; -import { DashboardModel } from '../../state'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { DashboardSettings } from './DashboardSettings'; @@ -27,7 +27,7 @@ setBackendSrv({ describe('DashboardSettings', () => { it('pressing escape navigates away correctly', async () => { - const dashboard = new DashboardModel( + const dashboard = createDashboardModelFixture( { title: 'Foo', }, diff --git a/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx index 227765f674d..63fe537daa2 100644 --- a/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx @@ -12,7 +12,7 @@ import { BackendSrv, setBackendSrv } from '@grafana/runtime'; import { GrafanaContext } from 'app/core/context/GrafanaContext'; import { configureStore } from '../../../../store/configureStore'; -import { DashboardModel } from '../../state'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { GeneralSettingsUnconnected as GeneralSettings, Props } from './GeneralSettings'; @@ -23,13 +23,16 @@ setBackendSrv({ const setupTestContext = (options: Partial) => { const store = configureStore(); const defaults: Props = { - dashboard: new DashboardModel( + dashboard: createDashboardModelFixture( { title: 'test dashboard title', description: 'test dashboard description', timepicker: { refresh_intervals: ['5s', '10s', '30s', '1m', '5m', '15m', '30m', '1h', '2h', '1d', '2d'], time_options: ['5m', '15m', '1h', '6h', '12h', '24h', '2d', '7d', '30d'], + collapse: true, + enable: true, + hidden: false, }, timezone: 'utc', }, diff --git a/public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx index c6716812b13..b4dbc4d30ea 100644 --- a/public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx @@ -12,6 +12,7 @@ import { GrafanaContext } from 'app/core/context/GrafanaContext'; import { configureStore } from '../../../../store/configureStore'; import { DashboardModel } from '../../state'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { DashboardSettings } from './DashboardSettings'; @@ -37,7 +38,7 @@ function setup(dashboard: DashboardModel) { } function buildTestDashboard() { - return new DashboardModel({ + return createDashboardModelFixture({ links: [ { asDropdown: false, @@ -87,7 +88,7 @@ describe('LinksSettings', () => { }; test('it renders a header and cta if no links', () => { - const linklessDashboard = new DashboardModel({ links: [] }); + const linklessDashboard = createDashboardModelFixture({ links: [] }); setup(linklessDashboard); expect(screen.getByRole('heading', { name: 'Links' })).toBeInTheDocument(); diff --git a/public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx index 9de7e2b0ff1..add18331246 100644 --- a/public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx @@ -9,7 +9,7 @@ import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; import { GrafanaContext } from 'app/core/context/GrafanaContext'; import { configureStore } from '../../../../store/configureStore'; -import { DashboardModel } from '../../state/DashboardModel'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { historySrv } from '../VersionHistory/HistorySrv'; import { VersionsSettings, VERSIONS_FETCH_LIMIT } from './VersionsSettings'; @@ -30,11 +30,11 @@ const queryByFullText = (text: string) => function setup() { const store = configureStore(); - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ id: 74, version: 11, - formatDate: jest.fn(() => 'date'), - getRelativeTime: jest.fn(() => 'time ago'), + // formatDate: jest.fn(() => 'date'), + // getRelativeTime: jest.fn(() => 'time ago'), }); const sectionNav = { diff --git a/public/app/features/dashboard/components/PanelEditor/OptionsPaneOptions.test.tsx b/public/app/features/dashboard/components/PanelEditor/OptionsPaneOptions.test.tsx index af96ecefe49..bef989f789b 100644 --- a/public/app/features/dashboard/components/PanelEditor/OptionsPaneOptions.test.tsx +++ b/public/app/features/dashboard/components/PanelEditor/OptionsPaneOptions.test.tsx @@ -16,7 +16,8 @@ import { selectors } from '@grafana/e2e-selectors'; import { getAllOptionEditors, getAllStandardFieldConfigs } from 'app/core/components/OptionsUI/registry'; import { getPanelPlugin } from 'app/features/plugins/__mocks__/pluginMocks'; -import { DashboardModel, PanelModel } from '../../state'; +import { PanelModel } from '../../state'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { OptionsPaneOptions } from './OptionsPaneOptions'; import { dataOverrideTooltipDescription, overrideRuleTooltipDescription } from './state/getOptionOverrides'; @@ -88,7 +89,7 @@ class OptionsPaneOptionsTestScenario { options: {}, }); - dashboard = new DashboardModel({}); + dashboard = createDashboardModelFixture(); store = mockStore({ dashboard: { panels: [] }, templating: { diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.test.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.test.tsx index 30b3f0ca4de..1bdc4169ed9 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.test.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.test.tsx @@ -18,7 +18,8 @@ import { import { getTimeSrv, TimeSrv, setTimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { PanelQueryRunner } from '../../../query/state/PanelQueryRunner'; -import { DashboardModel, PanelModel } from '../../state'; +import { PanelModel } from '../../state'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { PanelEditorTableView, Props } from './PanelEditorTableView'; @@ -53,14 +54,11 @@ function setupTestContext(options: Partial = {}) { getDisplayTitle: jest.fn(), runAllPanelQueries: jest.fn(), }), - dashboard: new DashboardModel({ + dashboard: createDashboardModelFixture({ id: 1, uid: 'super-unique-id', - panelInitialized: jest.fn(), - events: new EventBusSrv(), - meta: { - isPublic: false, - }, + // panelInitialized: jest.fn(), + // events: new EventBusSrv(), panels: [], }), plugin: { diff --git a/public/app/features/dashboard/components/PanelEditor/state/actions.test.ts b/public/app/features/dashboard/components/PanelEditor/state/actions.test.ts index 946dfc7f77d..21c5ee11372 100644 --- a/public/app/features/dashboard/components/PanelEditor/state/actions.test.ts +++ b/public/app/features/dashboard/components/PanelEditor/state/actions.test.ts @@ -1,8 +1,9 @@ +import { createDashboardModelFixture } from 'app/features/dashboard/state/__fixtures__/dashboardFixtures'; import { panelModelAndPluginReady, removePanel } from 'app/features/panel/state/reducers'; import { getPanelPlugin } from 'app/features/plugins/__mocks__/pluginMocks'; import { thunkTester } from '../../../../../../test/core/thunk/thunkTester'; -import { DashboardModel, PanelModel } from '../../../state'; +import { PanelModel } from '../../../state'; import { exitPanelEditor, initPanelEditor, skipPanelUpdate } from './actions'; import { closeEditor, initialState, PanelEditorState } from './reducers'; @@ -10,7 +11,7 @@ import { closeEditor, initialState, PanelEditorState } from './reducers'; describe('panelEditor actions', () => { describe('initPanelEditor', () => { it('initPanelEditor should create edit panel model as clone', async () => { - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ panels: [{ id: 12, type: 'graph' }], }); const sourcePanel = new PanelModel({ id: 12, type: 'graph' }); @@ -34,7 +35,7 @@ describe('panelEditor actions', () => { describe('panelEditorCleanUp', () => { it('should update source panel', async () => { const sourcePanel = new PanelModel({ id: 12, type: 'graph' }); - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ panels: [{ id: 12, type: 'graph' }], }); @@ -66,7 +67,7 @@ describe('panelEditor actions', () => { it('should dispatch panelModelAndPluginReady if type changed', async () => { const sourcePanel = new PanelModel({ id: 12, type: 'graph' }); - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ panels: [{ id: 12, type: 'graph' }], }); @@ -105,7 +106,7 @@ describe('panelEditor actions', () => { customFieldConfigs: {}, } as any; - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ panels: [{ id: 12, type: 'graph' }], }); @@ -138,7 +139,7 @@ describe('panelEditor actions', () => { sourcePanel.plugin = getPanelPlugin({}); sourcePanel.plugin.angularPanelCtrl = undefined; - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ panels: [{ id: 12, type: 'graph' }], }); @@ -168,7 +169,7 @@ describe('panelEditor actions', () => { sourcePanel.plugin = getPanelPlugin({}); sourcePanel.plugin.angularPanelCtrl = undefined; - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ panels: [{ id: 12, type: 'graph' }], }); @@ -207,7 +208,7 @@ describe('panelEditor actions', () => { sourcePanel.plugin = getPanelPlugin({}); sourcePanel.plugin.angularPanelCtrl = {}; - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ panels: [{ id: 12, type: 'graph' }], }); diff --git a/public/app/features/dashboard/components/SaveDashboard/SaveDashboardDrawer.test.tsx b/public/app/features/dashboard/components/SaveDashboard/SaveDashboardDrawer.test.tsx index 38406ee22d5..a978f97da2a 100644 --- a/public/app/features/dashboard/components/SaveDashboard/SaveDashboardDrawer.test.tsx +++ b/public/app/features/dashboard/components/SaveDashboard/SaveDashboardDrawer.test.tsx @@ -6,6 +6,7 @@ import { Provider } from 'react-redux'; import { configureStore } from 'app/store/configureStore'; import { DashboardModel } from '../../state'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { SaveDashboardDrawer } from './SaveDashboardDrawer'; @@ -30,7 +31,7 @@ jest.mock('app/core/services/backend_srv', () => ({ const store = configureStore(); const mockPost = jest.fn(); const buildMocks = () => ({ - dashboard: new DashboardModel({ + dashboard: createDashboardModelFixture({ uid: 'mockDashboardUid', version: 1, }), diff --git a/public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardAsForm.tsx b/public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardAsForm.tsx index 9420de8993d..b55b1657c12 100644 --- a/public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardAsForm.tsx +++ b/public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardAsForm.tsx @@ -31,7 +31,6 @@ const getSaveAsDashboardClone = (dashboard: DashboardModel) => { }); } - delete clone.autoUpdate; return clone; }; diff --git a/public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardForm.test.tsx b/public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardForm.test.tsx index 538b91de9d4..d25abff8e42 100644 --- a/public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardForm.test.tsx +++ b/public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardForm.test.tsx @@ -3,6 +3,7 @@ import userEvent from '@testing-library/user-event'; import React from 'react'; import { DashboardModel } from 'app/features/dashboard/state'; +import { createDashboardModelFixture } from 'app/features/dashboard/state/__fixtures__/dashboardFixtures'; import { SaveDashboardOptions } from '../types'; @@ -123,14 +124,14 @@ describe('SaveDashboardAsForm', () => { it('renders saved message draft if it was filled before', () => { render( {}} onSuccess={() => {}} onSubmit={async () => { return {}; }} saveModel={{ - clone: new DashboardModel({}), + clone: createDashboardModelFixture(), diff: {}, diffCount: 0, hasChanges: true, diff --git a/public/app/features/dashboard/components/ShareModal/ShareEmbed.test.tsx b/public/app/features/dashboard/components/ShareModal/ShareEmbed.test.tsx index 98a7f1bd03d..bd19caed810 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareEmbed.test.tsx +++ b/public/app/features/dashboard/components/ShareModal/ShareEmbed.test.tsx @@ -6,7 +6,8 @@ import { setEchoSrv } from '@grafana/runtime/src'; import config from 'app/core/config'; import { Echo } from '../../../../core/services/echo/Echo'; -import { DashboardModel, PanelModel } from '../../state'; +import { PanelModel } from '../../state'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { ShareEmbed } from './ShareEmbed'; @@ -68,7 +69,7 @@ describe('ShareEmbed', () => { }); it('generates the correct embed url for a dashboard', () => { - const mockDashboard = new DashboardModel({ + const mockDashboard = createDashboardModelFixture({ uid: 'mockDashboardUid', }); const mockPanel = new PanelModel({ @@ -86,7 +87,7 @@ describe('ShareEmbed', () => { it('generates the correct embed url for a dashboard set to the homepage in the grafana config', () => { mockLocationHref('http://dashboards.grafana.com/?orgId=1'); - const mockDashboard = new DashboardModel({ + const mockDashboard = createDashboardModelFixture({ uid: 'mockDashboardUid', }); const mockPanel = new PanelModel({ @@ -104,7 +105,7 @@ describe('ShareEmbed', () => { it('generates the correct embed url for a snapshot', () => { const mockSlug = 'mockSlug'; mockLocationHref(`http://dashboards.grafana.com/dashboard/snapshot/${mockSlug}?orgId=1`); - const mockDashboard = new DashboardModel({ + const mockDashboard = createDashboardModelFixture({ uid: 'mockDashboardUid', }); const mockPanel = new PanelModel({ @@ -122,7 +123,7 @@ describe('ShareEmbed', () => { it('generates the correct embed url for a scripted dashboard', () => { const mockSlug = 'scripted.js'; mockLocationHref(`http://dashboards.grafana.com/dashboard/script/${mockSlug}?orgId=1`); - const mockDashboard = new DashboardModel({ + const mockDashboard = createDashboardModelFixture({ uid: 'mockDashboardUid', }); const mockPanel = new PanelModel({ diff --git a/public/app/features/dashboard/components/ShareModal/ShareLink.test.tsx b/public/app/features/dashboard/components/ShareModal/ShareLink.test.tsx index be0e8bdcac8..71a9c3c0f0c 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareLink.test.tsx +++ b/public/app/features/dashboard/components/ShareModal/ShareLink.test.tsx @@ -11,7 +11,8 @@ import { initTemplateSrv } from '../../../../../test/helpers/initTemplateSrv'; import { Echo } from '../../../../core/services/echo/Echo'; import { variableAdapters } from '../../../variables/adapters'; import { createQueryVariableAdapter } from '../../../variables/query/adapter'; -import { DashboardModel, PanelModel } from '../../state'; +import { PanelModel } from '../../state'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { Props, ShareLink } from './ShareLink'; @@ -78,13 +79,20 @@ describe('ShareModal', () => { }); beforeEach(() => { + const defaultTimeRange = getDefaultTimeRange(); setUTCTimeZone(); mockLocationHref('http://server/#!/test'); config.rendererAvailable = true; config.bootData.user.orgId = 1; props = { panel: new PanelModel({ id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } }), - dashboard: new DashboardModel({ time: getDefaultTimeRange(), id: 1 }), + dashboard: createDashboardModelFixture({ + time: { + from: defaultTimeRange.from.toISOString(), + to: defaultTimeRange.to.toISOString(), + }, + id: 1, + }), }; }); @@ -175,7 +183,7 @@ describe('when appUrl is set in the grafana config', () => { }); it('should render the correct link', async () => { - const mockDashboard = new DashboardModel({ + const mockDashboard = createDashboardModelFixture({ uid: 'mockDashboardUid', id: 1, }); diff --git a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.test.tsx b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.test.tsx index 9ca5c38289b..5d087bdf0a5 100644 --- a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.test.tsx +++ b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.test.tsx @@ -8,11 +8,13 @@ import 'whatwg-fetch'; import { BootData, DataQuery } from '@grafana/data/src'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { setEchoSrv } from '@grafana/runtime/src'; +import { Panel } from '@grafana/schema'; import config from 'app/core/config'; import { backendSrv } from 'app/core/services/backend_srv'; import { contextSrv } from 'app/core/services/context_srv'; import { Echo } from 'app/core/services/echo/Echo'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; +import { createDashboardModelFixture } from 'app/features/dashboard/state/__fixtures__/dashboardFixtures'; import { configureStore } from 'app/store/configureStore'; import { ShareModal } from '../ShareModal'; @@ -71,7 +73,7 @@ beforeAll(() => { beforeEach(() => { config.featureToggles.publicDashboards = true; - mockDashboard = new DashboardModel({ + mockDashboard = createDashboardModelFixture({ uid: 'mockDashboardUid', }); @@ -203,8 +205,8 @@ describe('SharePublic - New config setup', () => { datasource: { type: 'notSupportedDatasource', uid: 'abc123' }, } as DataQuery, ] as DataQuery[], - } as PanelModel; - const dashboard = new DashboardModel({ + } as unknown as Panel; + const dashboard = createDashboardModelFixture({ id: 1, panels: [panelModel], }); diff --git a/public/app/features/dashboard/components/VersionHistory/HistorySrv.test.ts b/public/app/features/dashboard/components/VersionHistory/HistorySrv.test.ts index ced192487d8..8571795c054 100644 --- a/public/app/features/dashboard/components/VersionHistory/HistorySrv.test.ts +++ b/public/app/features/dashboard/components/VersionHistory/HistorySrv.test.ts @@ -1,4 +1,5 @@ import { DashboardModel } from '../../state/DashboardModel'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { HistorySrv } from './HistorySrv'; import { restore, versions } from './__mocks__/dashboardHistoryMocks'; @@ -25,8 +26,8 @@ describe('historySrv', () => { let historySrv = new HistorySrv(); - const dash = new DashboardModel({ uid: '_U4zObQMz' }); - const emptyDash = new DashboardModel({}); + const dash = createDashboardModelFixture({ uid: '_U4zObQMz' }); + const emptyDash = createDashboardModelFixture(); const historyListOpts = { limit: 10, start: 0 }; beforeEach(() => { diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.test.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.test.tsx index f6376c2e2eb..9a74f820677 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.test.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.test.tsx @@ -2,7 +2,8 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; import { createEmptyQueryResponse } from '../../../explore/state/utils'; -import { DashboardModel, PanelModel } from '../../state'; +import { PanelModel } from '../../state'; +import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures'; import { PanelHeader } from './PanelHeader'; @@ -16,7 +17,7 @@ let panelModel = new PanelModel({ let panelData = createEmptyQueryResponse(); describe('Panel Header', () => { - const dashboardModel = new DashboardModel({}, { publicDashboardAccessToken: 'abc123' }); + const dashboardModel = createDashboardModelFixture({}, { publicDashboardAccessToken: 'abc123' }); it('will render header title but not render dropdown icon when dashboard is being viewed publicly', () => { window.history.pushState({}, 'Test Title', '/public-dashboards/abc123'); @@ -29,7 +30,7 @@ describe('Panel Header', () => { }); it('will render header title and dropdown icon when dashboard is not being viewed publicly', () => { - const dashboardModel = new DashboardModel({}, { publicDashboardAccessToken: '' }); + const dashboardModel = createDashboardModelFixture({}, { publicDashboardAccessToken: '' }); window.history.pushState({}, 'Test Title', '/d/abc/123'); render( diff --git a/public/app/features/dashboard/state/DashboardMigrator.test.ts b/public/app/features/dashboard/state/DashboardMigrator.test.ts index 93f16735f99..ac976d8d296 100644 --- a/public/app/features/dashboard/state/DashboardMigrator.test.ts +++ b/public/app/features/dashboard/state/DashboardMigrator.test.ts @@ -64,6 +64,7 @@ describe('DashboardModel', () => { { type: 'annotations', enable: true, annotations: [{ name: 'old' }] }, ], panels: [ + // @ts-expect-error { type: 'graph', legend: true, @@ -87,6 +88,7 @@ describe('DashboardModel', () => { { type: 'singlestat', legend: true, + // @ts-expect-error thresholds: '10,20,30', colors: ['#FF0000', 'green', 'orange'], aliasYAxis: { test: 2 }, @@ -95,6 +97,7 @@ describe('DashboardModel', () => { }, { type: 'singlestat', + // @ts-expect-error thresholds: '10,20,30', colors: ['#FF0000', 'green', 'orange'], gauge: { @@ -106,6 +109,7 @@ describe('DashboardModel', () => { }, { type: 'table', + // @ts-expect-error legend: true, styles: [{ thresholds: ['10', '20', '30'] }, { thresholds: ['100', '200', '300'] }], targets: [{ refId: 'A' }, {}], @@ -210,6 +214,7 @@ describe('DashboardModel', () => { panels: [ { type: 'graph', + // @ts-expect-error y_formats: ['kbyte', 'ms'], grid: { threshold1: 200, @@ -468,6 +473,7 @@ describe('DashboardModel', () => { const model = { panels: [{ minSpan: 8 }], }; + // @ts-expect-error const dashboard = new DashboardModel(model); expect(dashboard.panels[0].maxPerRow).toBe(3); }); @@ -481,6 +487,7 @@ describe('DashboardModel', () => { panels: [ { links: [ + // @ts-expect-error { url: 'http://mylink.com', keepTime: true, @@ -488,23 +495,28 @@ describe('DashboardModel', () => { }, { url: 'http://mylink.com?existingParam', + // @ts-expect-error params: 'customParam', title: 'test', }, + // @ts-expect-error { url: 'http://mylink.com?existingParam', includeVars: true, title: 'test', }, { + // @ts-expect-error dashboard: 'my other dashboard', title: 'test', }, { + // @ts-expect-error dashUri: '', title: 'test', }, { + // @ts-expect-error type: 'dashboard', keepTime: true, }, @@ -536,6 +548,7 @@ describe('DashboardModel', () => { beforeEach(() => { model = new DashboardModel({ panels: [ + // @ts-expect-error { //graph panel options: { @@ -549,6 +562,7 @@ describe('DashboardModel', () => { ], }, }, + // @ts-expect-error { // panel with field options options: { @@ -601,6 +615,7 @@ describe('DashboardModel', () => { beforeEach(() => { model = new DashboardModel({ panels: [ + // @ts-expect-error { //graph panel options: { @@ -611,6 +626,7 @@ describe('DashboardModel', () => { ], }, }, + // @ts-expect-error { // panel with field options options: { @@ -649,6 +665,7 @@ describe('DashboardModel', () => { templating: { list: [ { + // @ts-expect-error multi: false, current: { value: ['value'], @@ -656,6 +673,7 @@ describe('DashboardModel', () => { }, }, { + // @ts-expect-error multi: true, current: { value: ['value'], @@ -697,6 +715,7 @@ describe('DashboardModel', () => { list: [ { type: 'query', + // @ts-expect-error tags: ['Africa', 'America', 'Asia', 'Europe'], tagsQuery: 'select datacenter from x', tagValuesQuery: 'select value from x where datacenter = xyz', @@ -704,6 +723,7 @@ describe('DashboardModel', () => { }, { type: 'query', + // @ts-expect-error current: { tags: [ { @@ -729,6 +749,7 @@ describe('DashboardModel', () => { }, { type: 'query', + // @ts-expect-error tags: [ { text: 'Africa', selected: false }, { text: 'America', selected: true }, @@ -783,10 +804,12 @@ describe('DashboardModel', () => { id: 2, type: 'text', title: 'Angular Text Panel', + // @ts-expect-error content: '# Angular Text Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n', mode: 'markdown', }, + // @ts-expect-error { id: 3, type: 'text2', @@ -797,6 +820,7 @@ describe('DashboardModel', () => { '# React Text Panel from scratch\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text', }, }, + // @ts-expect-error { id: 4, type: 'text2', @@ -867,24 +891,28 @@ describe('DashboardModel', () => { type: 'query', hide: VariableHide.dontHide, datasource: null, + // @ts-expect-error allFormat: '', }, { type: 'query', hide: VariableHide.hideLabel, datasource: null, + // @ts-expect-error allFormat: '', }, { type: 'query', hide: VariableHide.hideVariable, datasource: null, + // @ts-expect-error allFormat: '', }, { type: 'constant', hide: VariableHide.dontHide, query: 'default value', + // @ts-expect-error current: { selected: true, text: 'A', value: 'B' }, options: [{ selected: true, text: 'A', value: 'B' }], datasource: null, @@ -894,6 +922,7 @@ describe('DashboardModel', () => { type: 'constant', hide: VariableHide.hideLabel, query: 'default value', + // @ts-expect-error current: { selected: true, text: 'A', value: 'B' }, options: [{ selected: true, text: 'A', value: 'B' }], datasource: null, @@ -903,6 +932,7 @@ describe('DashboardModel', () => { type: 'constant', hide: VariableHide.hideVariable, query: 'default value', + // @ts-expect-error current: { selected: true, text: 'A', value: 'B' }, options: [{ selected: true, text: 'A', value: 'B' }], datasource: null, @@ -967,79 +997,93 @@ describe('DashboardModel', () => { { type: 'query', name: 'variable_with_never_refresh_with_options', + // @ts-expect-error options: [{ text: 'A', value: 'A' }], refresh: 0, }, { type: 'query', name: 'variable_with_never_refresh_without_options', + // @ts-expect-error options: [], refresh: 0, }, { type: 'query', name: 'variable_with_dashboard_refresh_with_options', + // @ts-expect-error options: [{ text: 'A', value: 'A' }], refresh: 1, }, { type: 'query', name: 'variable_with_dashboard_refresh_without_options', + // @ts-expect-error options: [], refresh: 1, }, { type: 'query', name: 'variable_with_timerange_refresh_with_options', + // @ts-expect-error options: [{ text: 'A', value: 'A' }], refresh: 2, }, { type: 'query', name: 'variable_with_timerange_refresh_without_options', + // @ts-expect-error options: [], refresh: 2, }, { type: 'query', name: 'variable_with_no_refresh_with_options', + // @ts-expect-error options: [{ text: 'A', value: 'A' }], }, { type: 'query', name: 'variable_with_no_refresh_without_options', + // @ts-expect-error options: [], }, { type: 'query', name: 'variable_with_unknown_refresh_with_options', + // @ts-expect-error options: [{ text: 'A', value: 'A' }], refresh: 2001, }, { type: 'query', name: 'variable_with_unknown_refresh_without_options', + // @ts-expect-error options: [], refresh: 2001, }, { type: 'custom', name: 'custom', + // @ts-expect-error options: [{ text: 'custom', value: 'custom' }], }, { type: 'textbox', name: 'textbox', + // @ts-expect-error options: [{ text: 'Hello', value: 'World' }], }, { type: 'datasource', name: 'datasource', + // @ts-expect-error options: [{ text: 'ds', value: 'ds' }], // fake example doesn't exist }, { type: 'interval', name: 'interval', + // @ts-expect-error options: [{ text: '1m', value: '1m' }], }, ], @@ -1112,10 +1156,12 @@ describe('DashboardModel', () => { fieldConfig: { defaults: { thresholds: { + // @ts-expect-error mode: 'absolute', steps: [ { color: 'green', + // @ts-expect-error value: null, }, { @@ -1128,12 +1174,14 @@ describe('DashboardModel', () => { { id: 0, text: '1', + // @ts-expect-error type: 1, value: 'up', }, { id: 1, text: 'BAD', + // @ts-expect-error type: 1, value: 'down', }, @@ -1142,6 +1190,7 @@ describe('DashboardModel', () => { id: 2, text: 'below 30', to: '30', + // @ts-expect-error type: 2, }, { @@ -1149,9 +1198,11 @@ describe('DashboardModel', () => { id: 3, text: '100', to: '100', + // @ts-expect-error type: 2, }, { + // @ts-expect-error type: 1, value: 'null', text: 'it is null', @@ -1243,6 +1294,7 @@ describe('DashboardModel', () => { panels: [ { type: 'timeseries', + // @ts-expect-error legend: true, options: { tooltipOptions: { mode: 'multi' }, @@ -1250,6 +1302,7 @@ describe('DashboardModel', () => { }, { type: 'xychart', + // @ts-expect-error legend: true, options: { tooltipOptions: { mode: 'single' }, @@ -1281,6 +1334,7 @@ describe('DashboardModel', () => { { type: 'singlestat', legend: true, + // @ts-expect-error thresholds: '10,20,30', colors: ['#FF0000', 'green', 'orange'], aliasYAxis: { test: 2 }, @@ -1342,6 +1396,7 @@ describe('DashboardModel', () => { { type: 'singlestat', legend: true, + // @ts-expect-error thresholds: '10,20,30', colors: ['#FF0000', 'green', 'orange'], aliasYAxis: { test: 2 }, @@ -1424,6 +1479,7 @@ describe('DashboardModel', () => { { id: 1, type: 'timeseries', + // @ts-expect-error panels: [ { id: 2, @@ -1462,6 +1518,7 @@ describe('DashboardModel', () => { { id: 1, type: 'timeseries', + // @ts-expect-error transformations: [{ id: 'labelsToFields' }], }, ], @@ -1493,6 +1550,7 @@ describe('DashboardModel', () => { annotations: { list: [ { + // @ts-expect-error actionPrefix: '', alarmNamePrefix: '', alias: '', @@ -1515,6 +1573,7 @@ describe('DashboardModel', () => { ], }, panels: [ + // @ts-expect-error { gridPos: { h: 8, @@ -1602,6 +1661,7 @@ describe('DashboardModel', () => { annotations: { list: [ { + // @ts-expect-error actionPrefix: '', alarmNamePrefix: '', alias: '', @@ -1636,6 +1696,7 @@ describe('DashboardModel', () => { title: 'DynamoDB', type: 'row', panels: [ + // @ts-expect-error { gridPos: { h: 8, @@ -1690,6 +1751,7 @@ describe('DashboardModel', () => { title: 'Panel Title', type: 'timeseries', }, + // @ts-expect-error { gridPos: { h: 8, @@ -1793,6 +1855,7 @@ describe('DashboardModel', () => { name: 'var', options: [{ text: 'A', value: 'A' }], refresh: 0, + // @ts-expect-error datasource: 'prom', }, ], @@ -1800,14 +1863,17 @@ describe('DashboardModel', () => { panels: [ { id: 1, + // @ts-expect-error datasource: 'prom', }, { id: 2, + // @ts-expect-error datasource: null, }, { id: 3, + // @ts-expect-error datasource: MIXED_DATASOURCE_NAME, targets: [ { @@ -1827,6 +1893,7 @@ describe('DashboardModel', () => { panels: [ { id: 6, + // @ts-expect-error datasource: 'prom', }, ], @@ -1873,6 +1940,7 @@ describe('DashboardModel', () => { panels: [ { id: 2, + // @ts-expect-error datasource: null, targets: [ { @@ -1894,6 +1962,7 @@ describe('DashboardModel', () => { test('preserves x axis visibility', () => { const model = new DashboardModel({ panels: [ + // @ts-expect-error { type: 'timeseries', fieldConfig: { @@ -1937,6 +2006,7 @@ describe('DashboardModel', () => { { type: 'query', name: 'var', + // @ts-expect-error options: [{ text: 'A', value: 'A' }], refresh: 0, datasource: null, @@ -1946,9 +2016,11 @@ describe('DashboardModel', () => { annotations: { list: [ { + // @ts-expect-error datasource: null, }, { + // @ts-expect-error datasource: 'prom', }, ], @@ -1956,6 +2028,7 @@ describe('DashboardModel', () => { panels: [ { id: 2, + // @ts-expect-error datasource: null, targets: [ { @@ -1963,6 +2036,7 @@ describe('DashboardModel', () => { }, ], }, + // @ts-expect-error { id: 3, targets: [ @@ -2007,6 +2081,7 @@ describe('DashboardModel', () => { beforeEach(() => { model = new DashboardModel({ panels: [ + // @ts-expect-error { id: 2, targets: [ @@ -2040,6 +2115,7 @@ describe('when generating the legend for a panel', () => { beforeEach(() => { model = new DashboardModel({ panels: [ + // @ts-expect-error { id: 0, options: { @@ -2052,6 +2128,7 @@ describe('when generating the legend for a panel', () => { }, }, }, + // @ts-expect-error { id: 1, options: { @@ -2064,6 +2141,7 @@ describe('when generating the legend for a panel', () => { }, }, }, + // @ts-expect-error { id: 2, options: { diff --git a/public/app/features/dashboard/state/DashboardModel.refresh.test.ts b/public/app/features/dashboard/state/DashboardModel.refresh.test.ts index 67d7555f9d2..14117453a5d 100644 --- a/public/app/features/dashboard/state/DashboardModel.refresh.test.ts +++ b/public/app/features/dashboard/state/DashboardModel.refresh.test.ts @@ -2,8 +2,8 @@ import { appEvents } from '../../../core/core'; import { VariablesChanged } from '../../variables/types'; import { getTimeSrv, setTimeSrv } from '../services/TimeSrv'; -import { DashboardModel } from './DashboardModel'; import { PanelModel } from './PanelModel'; +import { createDashboardModelFixture } from './__fixtures__/dashboardFixtures'; function getTestContext({ usePanelInEdit, @@ -12,7 +12,7 @@ function getTestContext({ }: { usePanelInEdit?: boolean; usePanelInView?: boolean; refreshAll?: boolean } = {}) { jest.clearAllMocks(); - const dashboard = new DashboardModel({}); + const dashboard = createDashboardModelFixture(); const startRefreshMock = jest.fn(); dashboard.startRefresh = startRefreshMock; const panelInView = new PanelModel({ id: 99 }); diff --git a/public/app/features/dashboard/state/DashboardModel.test.ts b/public/app/features/dashboard/state/DashboardModel.test.ts index 9d154c691bf..1a0e6822d32 100644 --- a/public/app/features/dashboard/state/DashboardModel.test.ts +++ b/public/app/features/dashboard/state/DashboardModel.test.ts @@ -1,5 +1,7 @@ import { keys as _keys } from 'lodash'; +import { VariableHide } from '@grafana/data'; +import { defaultVariableModel } from '@grafana/schema'; import { contextSrv } from 'app/core/services/context_srv'; import { getDashboardModel } from '../../../../test/helpers/getDashboardModel'; @@ -11,6 +13,13 @@ import { setTimeSrv, TimeSrv } from '../services/TimeSrv'; import { DashboardModel } from '../state/DashboardModel'; import { PanelModel } from '../state/PanelModel'; +import { + createAnnotationJSONFixture, + createDashboardModelFixture, + createPanelJSONFixture, + createVariableJSONFixture, +} from './__fixtures__/dashboardFixtures'; + jest.mock('app/core/services/context_srv'); const mockContextSrv = jest.mocked(contextSrv); @@ -26,7 +35,7 @@ describe('DashboardModel', () => { let model: DashboardModel; beforeEach(() => { - model = new DashboardModel({}, {}); + model = createDashboardModelFixture(); }); it('should have title', () => { @@ -47,8 +56,8 @@ describe('DashboardModel', () => { let model: DashboardModel; beforeEach(() => { - model = new DashboardModel({ - panels: [{ id: 5 }], + model = createDashboardModelFixture({ + panels: [createPanelJSONFixture({ id: 5 })], }); }); @@ -59,8 +68,7 @@ describe('DashboardModel', () => { describe('getSaveModelClone', () => { it('should sort keys', () => { - const model = new DashboardModel({}); - model.autoUpdate = null; + const model = createDashboardModelFixture(); const saveModel = model.getSaveModelClone(); const keys = _keys(saveModel); @@ -70,7 +78,7 @@ describe('DashboardModel', () => { }); it('should remove add panel panels', () => { - const model = new DashboardModel({}); + const model = createDashboardModelFixture(); model.addPanel({ type: 'add-panel', }); @@ -87,7 +95,7 @@ describe('DashboardModel', () => { }); it('should save model in edit mode', () => { - const model = new DashboardModel({}); + const model = createDashboardModelFixture(); model.addPanel({ type: 'graph' }); const panel = model.initEditPanel(model.panels[0]); @@ -105,7 +113,7 @@ describe('DashboardModel', () => { let dashboard: DashboardModel; beforeEach(() => { - dashboard = new DashboardModel({}); + dashboard = createDashboardModelFixture(); }); it('adding panel should new up panel model', () => { @@ -148,7 +156,7 @@ describe('DashboardModel', () => { let model: DashboardModel; beforeEach(() => { - model = new DashboardModel({ editable: false }); + model = createDashboardModelFixture({ editable: false }); }); it('Should set meta canEdit and canSave to false', () => { @@ -167,12 +175,11 @@ describe('DashboardModel', () => { let target: any; beforeEach(() => { - model = new DashboardModel({ + model = createDashboardModelFixture({ + schemaVersion: 1, panels: [ - { + createPanelJSONFixture({ type: 'graph', - grid: {}, - yaxes: [{}, {}], targets: [ { alias: '$tag_datacenter $tag_source $col', @@ -211,7 +218,7 @@ describe('DashboardModel', () => { ], }, ], - }, + }), ], }); @@ -233,13 +240,9 @@ describe('DashboardModel', () => { let model: DashboardModel; beforeEach(() => { - model = new DashboardModel({ - annotations: { - enable: true, - }, - templating: { - enable: true, - }, + model = createDashboardModelFixture({ + annotations: {}, + templating: {}, }); }); @@ -258,7 +261,7 @@ describe('DashboardModel', () => { let dashboard: DashboardModel; beforeEach(() => { - dashboard = new DashboardModel({ timezone: 'utc' }); + dashboard = createDashboardModelFixture({ timezone: 'utc' }); }); it('Should format timestamp with second resolution by default', () => { @@ -278,7 +281,7 @@ describe('DashboardModel', () => { let model: DashboardModel; beforeEach(() => { - model = new DashboardModel({}); + model = createDashboardModelFixture(); }); it('should not show submenu', () => { @@ -290,9 +293,21 @@ describe('DashboardModel', () => { let model: DashboardModel; beforeEach(() => { - model = new DashboardModel({ + model = createDashboardModelFixture({ + schemaVersion: 30, annotations: { - list: [{}], + list: [ + { + datasource: { uid: 'fake-uid', type: 'prometheus' }, + showIn: 0, + name: 'Fake annotation', + type: 'dashboard', + iconColor: 'rgba(0, 211, 255, 1)', + enable: true, + hide: false, + builtIn: 0, + }, + ], }, }); }); @@ -306,10 +321,10 @@ describe('DashboardModel', () => { let model: DashboardModel; beforeEach(() => { - model = new DashboardModel( + model = createDashboardModelFixture( { templating: { - list: [{}], + list: [createVariableJSONFixture({})], }, }, {}, @@ -327,9 +342,14 @@ describe('DashboardModel', () => { let model: DashboardModel; beforeEach(() => { - model = new DashboardModel({ + model = createDashboardModelFixture({ templating: { - list: [{ hide: 2 }], + list: [ + { + ...defaultVariableModel, + hide: VariableHide.hideVariable, + }, + ], }, }); }); @@ -343,9 +363,9 @@ describe('DashboardModel', () => { let dashboard: DashboardModel; beforeEach(() => { - dashboard = new DashboardModel({ + dashboard = createDashboardModelFixture({ annotations: { - list: [{ hide: true }], + list: [createAnnotationJSONFixture({ hide: true })], }, }); }); @@ -359,13 +379,13 @@ describe('DashboardModel', () => { let dashboard: DashboardModel; beforeEach(() => { - dashboard = new DashboardModel({ + dashboard = createDashboardModelFixture({ panels: [ - { id: 1, type: 'graph', gridPos: { x: 0, y: 0, w: 24, h: 2 } }, - { id: 2, type: 'row', gridPos: { x: 0, y: 2, w: 24, h: 2 } }, - { id: 3, type: 'graph', gridPos: { x: 0, y: 4, w: 12, h: 2 } }, - { id: 4, type: 'graph', gridPos: { x: 12, y: 4, w: 12, h: 2 } }, - { id: 5, type: 'row', gridPos: { x: 0, y: 6, w: 24, h: 2 } }, + createPanelJSONFixture({ id: 1, type: 'graph', gridPos: { x: 0, y: 0, w: 24, h: 2 } }), + createPanelJSONFixture({ id: 2, type: 'row', gridPos: { x: 0, y: 2, w: 24, h: 2 } }), + createPanelJSONFixture({ id: 3, type: 'graph', gridPos: { x: 0, y: 4, w: 12, h: 2 } }), + createPanelJSONFixture({ id: 4, type: 'graph', gridPos: { x: 12, y: 4, w: 12, h: 2 } }), + createPanelJSONFixture({ id: 5, type: 'row', gridPos: { x: 0, y: 6, w: 24, h: 2 } }), ], }); dashboard.toggleRow(dashboard.panels[1]); @@ -410,7 +430,7 @@ describe('DashboardModel', () => { let dashboard: DashboardModel; beforeEach(() => { - dashboard = new DashboardModel({ + dashboard = createDashboardModelFixture({ panels: [ { id: 1, type: 'graph', gridPos: { x: 0, y: 0, w: 24, h: 6 } }, { @@ -423,7 +443,7 @@ describe('DashboardModel', () => { { id: 4, type: 'graph', gridPos: { x: 12, y: 7, w: 12, h: 2 } }, ], }, - { id: 5, type: 'row', gridPos: { x: 0, y: 7, w: 1, h: 1 } }, + { id: 5, type: 'row', collapsed: false, panels: [], gridPos: { x: 0, y: 7, w: 1, h: 1 } }, ], }); dashboard.toggleRow(dashboard.panels[1]); @@ -481,7 +501,7 @@ describe('DashboardModel', () => { let dashboard: DashboardModel; beforeEach(() => { - dashboard = new DashboardModel({ + dashboard = createDashboardModelFixture({ panels: [ { id: 1, type: 'graph', gridPos: { x: 0, y: 0, w: 24, h: 6 } }, { @@ -490,11 +510,14 @@ describe('DashboardModel', () => { gridPos: { x: 0, y: 6, w: 24, h: 1 }, collapsed: true, panels: [ + // this whole test is about dealing with out-of-spec (or at least ambigious) data... + //@ts-expect-error { id: 3, type: 'graph', gridPos: { w: 12, h: 2 } }, + //@ts-expect-error { id: 4, type: 'graph', gridPos: { w: 12, h: 2 } }, ], }, - { id: 5, type: 'row', gridPos: { x: 0, y: 7, w: 1, h: 1 } }, + { id: 5, type: 'row', collapsed: false, panels: [], gridPos: { x: 0, y: 7, w: 1, h: 1 } }, ], }); dashboard.toggleRow(dashboard.panels[1]); @@ -523,7 +546,7 @@ describe('DashboardModel', () => { beforeEach(() => { consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); - model = new DashboardModel({ + model = createDashboardModelFixture({ time: { from: 'now-6h', to: 'now', @@ -838,14 +861,13 @@ describe('DashboardModel', () => { let model: DashboardModel; beforeEach(() => { - const data = { + model = createDashboardModelFixture({ panels: [ { id: 1, type: 'graph', gridPos: { x: 0, y: 0, w: 24, h: 2 }, legend: { show: true } }, { id: 3, type: 'graph', gridPos: { x: 0, y: 4, w: 12, h: 2 }, legend: { show: false } }, { id: 4, type: 'graph', gridPos: { x: 12, y: 4, w: 12, h: 2 }, legend: { show: false } }, ], - }; - model = new DashboardModel(data); + }); }); it('toggleLegendsForAll should toggle all legends on on first execution', () => { @@ -876,7 +898,7 @@ describe('DashboardModel', () => { `( 'when called with canEdit:{$canEdit}, canMakeEditable:{$canMakeEditable}, canAdd:{$canAdd} and expected:{$expected}', ({ canEdit, canMakeEditable, canAdd, expected }) => { - const dashboard = new DashboardModel( + const dashboard = createDashboardModelFixture( {}, { annotationsPermissions: { @@ -909,7 +931,7 @@ describe('DashboardModel', () => { `( 'when called with canEdit:{$canEdit}, canMakeEditable:{$canMakeEditable}, canEditWithOrgPermission:{$canEditWithOrgPermission} and expected:{$expected}', ({ canEdit, canMakeEditable, canEditWithOrgPermission, expected }) => { - const dashboard = new DashboardModel( + const dashboard = createDashboardModelFixture( {}, { annotationsPermissions: { @@ -940,7 +962,7 @@ describe('DashboardModel', () => { `( 'when called with canEdit:{$canEdit}, canMakeEditable:{$canMakeEditable}, canEditWithDashboardPermission:{$canEditWithDashboardPermission} and expected:{$expected}', ({ canEdit, canMakeEditable, canEditWithDashboardPermission, expected }) => { - const dashboard = new DashboardModel( + const dashboard = createDashboardModelFixture( {}, { annotationsPermissions: { @@ -973,7 +995,7 @@ describe('DashboardModel', () => { `( 'when called with canEdit:{$canEdit}, canMakeEditable:{$canMakeEditable}, canDeleteWithOrgPermission:{$canDeleteWithOrgPermission} and expected:{$expected}', ({ canEdit, canMakeEditable, canDeleteWithOrgPermission, expected }) => { - const dashboard = new DashboardModel( + const dashboard = createDashboardModelFixture( {}, { annotationsPermissions: { @@ -1004,7 +1026,7 @@ describe('DashboardModel', () => { `( 'when called with canEdit:{$canEdit}, canMakeEditable:{$canMakeEditable}, canDeleteWithDashboardPermission:{$canDeleteWithDashboardPermission} and expected:{$expected}', ({ canEdit, canMakeEditable, canDeleteWithDashboardPermission, expected }) => { - const dashboard = new DashboardModel( + const dashboard = createDashboardModelFixture( {}, { annotationsPermissions: { @@ -1025,9 +1047,9 @@ describe('DashboardModel', () => { describe('canEditPanel', () => { it('returns false if the dashboard cannot be edited', () => { - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ panels: [ - { id: 1, type: 'row', gridPos: { x: 0, y: 0, w: 24, h: 6 } }, + { id: 1, type: 'row', collapsed: false, panels: [], gridPos: { x: 0, y: 0, w: 24, h: 6 } }, { id: 2, type: 'graph', gridPos: { x: 0, y: 7, w: 12, h: 2 } }, ], }); @@ -1037,9 +1059,9 @@ describe('DashboardModel', () => { }); it('returns false if no panel is passed in', () => { - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ panels: [ - { id: 1, type: 'row', gridPos: { x: 0, y: 0, w: 24, h: 6 } }, + { id: 1, type: 'row', collapsed: false, panels: [], gridPos: { x: 0, y: 0, w: 24, h: 6 } }, { id: 2, type: 'graph', gridPos: { x: 0, y: 7, w: 12, h: 2 } }, ], }); @@ -1047,9 +1069,9 @@ describe('DashboardModel', () => { }); it('returns false if the panel is a repeat', () => { - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ panels: [ - { id: 1, type: 'row', gridPos: { x: 0, y: 0, w: 24, h: 6 } }, + { id: 1, type: 'row', collapsed: false, panels: [], gridPos: { x: 0, y: 0, w: 24, h: 6 } }, { id: 2, type: 'graph', gridPos: { x: 0, y: 7, w: 12, h: 2 } }, { id: 3, type: 'graph', gridPos: { x: 0, y: 7, w: 12, h: 2 }, repeatPanelId: 2 }, ], @@ -1059,9 +1081,9 @@ describe('DashboardModel', () => { }); it('returns false if the panel is a row', () => { - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ panels: [ - { id: 1, type: 'row', gridPos: { x: 0, y: 0, w: 24, h: 6 } }, + { id: 1, type: 'row', collapsed: false, panels: [], gridPos: { x: 0, y: 0, w: 24, h: 6 } }, { id: 2, type: 'graph', gridPos: { x: 0, y: 7, w: 12, h: 2 } }, ], }); @@ -1070,9 +1092,9 @@ describe('DashboardModel', () => { }); it('returns true otherwise', () => { - const dashboard = new DashboardModel({ + const dashboard = createDashboardModelFixture({ panels: [ - { id: 1, type: 'row', gridPos: { x: 0, y: 0, w: 24, h: 6 } }, + { id: 1, type: 'row', collapsed: false, panels: [], gridPos: { x: 0, y: 0, w: 24, h: 6 } }, { id: 2, type: 'graph', gridPos: { x: 0, y: 7, w: 12, h: 2 } }, ], }); @@ -1085,7 +1107,7 @@ describe('DashboardModel', () => { describe('exitViewPanel', () => { function getTestContext() { const panel: any = { setIsViewing: jest.fn() }; - const dashboard = new DashboardModel({}); + const dashboard = createDashboardModelFixture(); dashboard.startRefresh = jest.fn(); dashboard.panelInView = panel; @@ -1122,7 +1144,7 @@ describe('exitViewPanel', () => { describe('exitPanelEditor', () => { function getTestContext(pauseAutoRefresh = false) { const panel: any = { destroy: jest.fn() }; - const dashboard = new DashboardModel({}); + const dashboard = createDashboardModelFixture(); const timeSrvMock = { pauseAutoRefresh: jest.fn(), resumeAutoRefresh: jest.fn(), @@ -1172,7 +1194,7 @@ describe('exitPanelEditor', () => { describe('initEditPanel', () => { function getTestContext() { - const dashboard = new DashboardModel({}); + const dashboard = createDashboardModelFixture(); const timeSrvMock = { pauseAutoRefresh: jest.fn(), resumeAutoRefresh: jest.fn(), diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index cc3a0fdeaf3..3801ffc85e5 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -17,6 +17,7 @@ import { UrlQueryValue, } from '@grafana/data'; import { RefreshEvent, TimeRangeUpdatedEvent } from '@grafana/runtime'; +import { Dashboard } from '@grafana/schema'; import { DEFAULT_ANNOTATION_COLOR } from '@grafana/ui'; import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT, REPEAT_DIR_VERTICAL } from 'app/core/constants'; import { contextSrv } from 'app/core/services/context_srv'; @@ -68,9 +69,9 @@ export interface DashboardLink { export class DashboardModel implements TimeModel { id: any; - uid: string; + // TODO: use propert type and fix all the places where uid is set to null + uid: any; title: string; - autoUpdate: any; description: any; tags: any; style: any; @@ -125,17 +126,13 @@ export class DashboardModel implements TimeModel { lastRefresh: true, }; - constructor(data: any, meta?: DashboardMeta, private getVariablesFromState: GetVariables = getVariablesByKey) { - if (!data) { - data = {}; - } - + constructor(data: Dashboard, meta?: DashboardMeta, private getVariablesFromState: GetVariables = getVariablesByKey) { this.events = new EventBusSrv(); this.id = data.id || null; + // UID is not there for newly created dashboards this.uid = data.uid || null; - this.revision = data.revision; + this.revision = data.revision || 1; this.title = data.title ?? 'No Title'; - this.autoUpdate = data.autoUpdate; this.description = data.description; this.tags = data.tags ?? []; this.style = data.style ?? 'dark'; diff --git a/public/app/features/dashboard/state/__fixtures__/dashboardFixtures.ts b/public/app/features/dashboard/state/__fixtures__/dashboardFixtures.ts new file mode 100644 index 00000000000..ae33fe267a0 --- /dev/null +++ b/public/app/features/dashboard/state/__fixtures__/dashboardFixtures.ts @@ -0,0 +1,68 @@ +import { + AnnotationQuery, + Dashboard, + defaultDashboardCursorSync, + defaultVariableModel, + GraphPanel, + Panel, + VariableModel, +} from '@grafana/schema'; +import { GetVariables } from 'app/features/variables/state/selectors'; +import { DashboardMeta } from 'app/types'; + +import { DashboardModel } from '../DashboardModel'; + +export function createDashboardModelFixture( + dashboardInput: Partial = {}, + meta?: DashboardMeta, + getVariablesFromState?: GetVariables +): DashboardModel { + const dashboardJson: Dashboard = { + editable: true, + graphTooltip: defaultDashboardCursorSync, + schemaVersion: 1, + revision: 1, + style: 'dark', + ...dashboardInput, + }; + + return new DashboardModel(dashboardJson, meta, getVariablesFromState); +} + +export function createPanelJSONFixture(panelInput: Partial = {}): Panel { + return { + fieldConfig: { + defaults: {}, + overrides: [], + }, + options: {}, + repeatDirection: 'h', + transformations: [], + transparent: false, + type: 'timeseries', + ...panelInput, + }; +} + +export function createAnnotationJSONFixture(annotationInput: Partial): AnnotationQuery { + return { + builtIn: 0, // ?? + datasource: { + type: 'foo', + uid: 'bar', + }, + showIn: 2, + enable: true, + type: 'anno', + ...annotationInput, + }; +} + +export function createVariableJSONFixture(annotationInput: Partial): VariableModel { + return { + ...defaultVariableModel, + name: 'foo.variable', + type: 'constant', + ...annotationInput, + }; +} diff --git a/public/app/features/dashboard/state/reducers.test.ts b/public/app/features/dashboard/state/reducers.test.ts index ab2fa4a8c9e..7cdd4300dff 100644 --- a/public/app/features/dashboard/state/reducers.test.ts +++ b/public/app/features/dashboard/state/reducers.test.ts @@ -1,6 +1,6 @@ import { DashboardInitPhase, DashboardState, OrgRole, PermissionLevel } from 'app/types'; -import { DashboardModel } from './DashboardModel'; +import { createDashboardModelFixture, createPanelJSONFixture } from './__fixtures__/dashboardFixtures'; import { dashboardInitCompleted, dashboardInitFailed, @@ -35,9 +35,9 @@ describe('dashboard reducer', () => { state = dashboardReducer( state, dashboardInitCompleted( - new DashboardModel({ + createDashboardModelFixture({ title: 'My dashboard', - panels: [{ id: 1 }, { id: 2 }], + panels: [createPanelJSONFixture({ id: 1 }), createPanelJSONFixture({ id: 2 })], }) ) ); diff --git a/public/app/features/dashboard/state/reducers.ts b/public/app/features/dashboard/state/reducers.ts index 4e57923e7c4..03e536c5a68 100644 --- a/public/app/features/dashboard/state/reducers.ts +++ b/public/app/features/dashboard/state/reducers.ts @@ -2,6 +2,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { PanelPlugin } from '@grafana/data'; import { AngularComponent } from '@grafana/runtime'; +import { Dashboard, defaultDashboard } from '@grafana/schema'; import { processAclItems } from 'app/core/utils/acl'; import { DashboardAclDTO, DashboardInitError, DashboardInitPhase, DashboardState } from 'app/types'; @@ -36,7 +37,12 @@ const dashboardSlice = createSlice({ state.initPhase = DashboardInitPhase.Failed; state.initError = action.payload; state.getModel = () => { - return new DashboardModel({ title: 'Dashboard init failed' }, { canSave: false, canEdit: false }); + // TODO this is a type conflict, + // we need to fix the defaultDashboard init type when generated by cue + return new DashboardModel( + { ...(defaultDashboard as Dashboard), title: 'Dashboard init failed' }, + { canSave: false, canEdit: false } + ); }; }, cleanUpDashboard: (state) => { diff --git a/public/app/features/dashboard/utils/getPanelMenu.test.ts b/public/app/features/dashboard/utils/getPanelMenu.test.ts index 0fae58b3e14..950b9dfde35 100644 --- a/public/app/features/dashboard/utils/getPanelMenu.test.ts +++ b/public/app/features/dashboard/utils/getPanelMenu.test.ts @@ -3,7 +3,8 @@ import config from 'app/core/config'; import * as actions from 'app/features/explore/state/main'; import { setStore } from 'app/store/store'; -import { DashboardModel, PanelModel } from '../state'; +import { PanelModel } from '../state'; +import { createDashboardModelFixture } from '../state/__fixtures__/dashboardFixtures'; import { getPanelMenu } from './getPanelMenu'; @@ -16,7 +17,7 @@ jest.mock('app/core/services/context_srv', () => ({ describe('getPanelMenu', () => { it('should return the correct panel menu items', () => { const panel = new PanelModel({}); - const dashboard = new DashboardModel({}); + const dashboard = createDashboardModelFixture({}); const menuItems = getPanelMenu(dashboard, panel); expect(menuItems).toMatchInlineSnapshot(` @@ -100,7 +101,7 @@ describe('getPanelMenu', () => { const scope: any = { $$childHead: { ctrl } }; const angularComponent: any = { getScope: () => scope }; const panel = new PanelModel({ isViewing: true }); - const dashboard = new DashboardModel({}); + const dashboard = createDashboardModelFixture({}); const menuItems = getPanelMenu(dashboard, panel, angularComponent); expect(menuItems).toMatchInlineSnapshot(` @@ -171,7 +172,7 @@ describe('getPanelMenu', () => { beforeAll(() => { const panel = new PanelModel({}); - const dashboard = new DashboardModel({}); + const dashboard = createDashboardModelFixture({}); const menuItems = getPanelMenu(dashboard, panel); explore = menuItems.find((item) => item.text === 'Explore') as PanelMenuItem; navigateSpy = jest.spyOn(actions, 'navigateToExplore'); diff --git a/public/app/features/dashboard/utils/panelMerge.test.ts b/public/app/features/dashboard/utils/panelMerge.test.ts index cc3f77a1d1d..f68d0dd0ddd 100644 --- a/public/app/features/dashboard/utils/panelMerge.test.ts +++ b/public/app/features/dashboard/utils/panelMerge.test.ts @@ -1,6 +1,8 @@ import { PanelModel } from '@grafana/data'; +import { FieldColorModeId, ThresholdsMode } from '@grafana/schema/src'; import { DashboardModel } from '../state/DashboardModel'; +import { createDashboardModelFixture, createPanelJSONFixture } from '../state/__fixtures__/dashboardFixtures'; describe('Merge dashbaord panels', () => { describe('simple changes', () => { @@ -8,35 +10,35 @@ describe('Merge dashbaord panels', () => { let rawPanels: PanelModel[]; beforeEach(() => { - dashboard = new DashboardModel({ + dashboard = createDashboardModelFixture({ title: 'simple title', panels: [ - { + createPanelJSONFixture({ id: 1, type: 'timeseries', - }, - { + }), + createPanelJSONFixture({ id: 2, type: 'timeseries', - }, - { + }), + createPanelJSONFixture({ id: 3, type: 'table', fieldConfig: { defaults: { thresholds: { - mode: 'absolute', + mode: ThresholdsMode.Absolute, steps: [ { color: 'green', value: -Infinity }, // save model has this as null { color: 'red', value: 80 }, ], }, mappings: [], - color: { mode: 'thresholds' }, + color: { mode: FieldColorModeId.Thresholds }, }, overrides: [], }, - }, + }), ], }); rawPanels = dashboard.getSaveModelClone().panels; diff --git a/public/app/features/explore/AddToDashboard/addToDashboard.test.ts b/public/app/features/explore/AddToDashboard/addToDashboard.test.ts index dd7f0ee43d4..8c98f81feb5 100644 --- a/public/app/features/explore/AddToDashboard/addToDashboard.test.ts +++ b/public/app/features/explore/AddToDashboard/addToDashboard.test.ts @@ -1,4 +1,5 @@ import { DataQuery, MutableDataFrame } from '@grafana/data'; +import { defaultDashboard } from '@grafana/schema'; import { backendSrv } from 'app/core/services/backend_srv'; import * as api from 'app/features/dashboard/state/initDashboard'; import { ExplorePanelData } from 'app/types'; @@ -53,6 +54,7 @@ describe('addPanelToDashboard', () => { const existingPanel = { prop: 'this should be kept' }; jest.spyOn(backendSrv, 'getDashboardByUid').mockResolvedValue({ dashboard: { + ...defaultDashboard, templating: { list: [] }, title: 'Previous panels should not be removed', uid: 'someUid', diff --git a/public/app/features/explore/AddToDashboard/index.test.tsx b/public/app/features/explore/AddToDashboard/index.test.tsx index 424950aea74..f4b5fe989f0 100644 --- a/public/app/features/explore/AddToDashboard/index.test.tsx +++ b/public/app/features/explore/AddToDashboard/index.test.tsx @@ -5,6 +5,7 @@ import { Provider } from 'react-redux'; import { DataQuery } from '@grafana/data'; import { locationService, setEchoSrv } from '@grafana/runtime'; +import { defaultDashboard } from '@grafana/schema'; import { backendSrv } from 'app/core/services/backend_srv'; import { contextSrv } from 'app/core/services/context_srv'; import { Echo } from 'app/core/services/echo/Echo'; @@ -194,7 +195,7 @@ describe('AddToDashboardButton', () => { const openSpy = jest.spyOn(global, 'open').mockReturnValue(true); jest.spyOn(backendSrv, 'getDashboardByUid').mockResolvedValue({ - dashboard: { templating: { list: [] }, title: 'Dashboard Title', uid: 'someUid' }, + dashboard: { ...defaultDashboard, templating: { list: [] }, title: 'Dashboard Title', uid: 'someUid' }, meta: {}, }); jest.spyOn(backendSrv, 'search').mockResolvedValue([ @@ -236,7 +237,7 @@ describe('AddToDashboardButton', () => { const pushSpy = jest.spyOn(locationService, 'push'); jest.spyOn(backendSrv, 'getDashboardByUid').mockResolvedValue({ - dashboard: { templating: { list: [] }, title: 'Dashboard Title', uid: 'someUid' }, + dashboard: { ...defaultDashboard, templating: { list: [] }, title: 'Dashboard Title', uid: 'someUid' }, meta: {}, }); jest.spyOn(backendSrv, 'search').mockResolvedValue([ diff --git a/public/app/features/library-panels/state/api.ts b/public/app/features/library-panels/state/api.ts index e82b2d5e4e1..7bb8f159ceb 100644 --- a/public/app/features/library-panels/state/api.ts +++ b/public/app/features/library-panels/state/api.ts @@ -1,5 +1,6 @@ import { lastValueFrom } from 'rxjs'; +import { defaultDashboard } from '@grafana/schema'; import { DashboardModel } from 'app/features/dashboard/state'; import { getBackendSrv } from '../../../core/services/backend_srv'; @@ -59,6 +60,7 @@ export async function getLibraryPanel(uid: string, isHandled = false): Promise ({ }), })); -const dashboardModel = new DashboardModel({ +const dashboardModel = createDashboardModelFixture({ panels: [{ id: 1, type: 'graph' }], }); diff --git a/public/app/features/query/state/queryAnalytics.test.ts b/public/app/features/query/state/queryAnalytics.test.ts index d23a537ac6d..839df12bf4c 100644 --- a/public/app/features/query/state/queryAnalytics.test.ts +++ b/public/app/features/query/state/queryAnalytics.test.ts @@ -10,7 +10,7 @@ import { } from '@grafana/data'; import { MetaAnalyticsEventName, reportMetaAnalytics } from '@grafana/runtime'; -import { DashboardModel } from '../../dashboard/state'; +import { createDashboardModelFixture } from '../../dashboard/state/__fixtures__/dashboardFixtures'; import { emitDataRequestEvent } from './queryAnalytics'; @@ -24,7 +24,7 @@ const datasource = { uid: 'test', } as DataSourceApi; -const dashboardModel = new DashboardModel( +const dashboardModel = createDashboardModelFixture( { id: 1, title: 'Test Dashboard', uid: 'test' }, { folderTitle: 'Test Folder' } ); diff --git a/public/app/features/query/state/runRequest.test.ts b/public/app/features/query/state/runRequest.test.ts index 52ec24b2481..92dcf7bac02 100644 --- a/public/app/features/query/state/runRequest.test.ts +++ b/public/app/features/query/state/runRequest.test.ts @@ -14,13 +14,13 @@ import { setEchoSrv } from '@grafana/runtime'; import { deepFreeze } from '../../../../test/core/redux/reducerTester'; import { Echo } from '../../../core/services/echo/Echo'; -import { DashboardModel } from '../../dashboard/state/DashboardModel'; +import { createDashboardModelFixture } from '../../dashboard/state/__fixtures__/dashboardFixtures'; import { runRequest } from './runRequest'; jest.mock('app/core/services/backend_srv'); -const dashboardModel = new DashboardModel({ +const dashboardModel = createDashboardModelFixture({ panels: [{ id: 1, type: 'graph' }], }); diff --git a/public/app/features/scenes/variables/interpolation/sceneInterpolator.ts b/public/app/features/scenes/variables/interpolation/sceneInterpolator.ts index 186a425b246..7ca068cf3e6 100644 --- a/public/app/features/scenes/variables/interpolation/sceneInterpolator.ts +++ b/public/app/features/scenes/variables/interpolation/sceneInterpolator.ts @@ -11,7 +11,7 @@ import { formatRegistry, FormatRegistryID, FormatVariable } from './formatRegist export type CustomFormatterFn = ( value: unknown, - legacyVariableModel: VariableModel, + legacyVariableModel: Partial, legacyDefaultFormatter?: CustomFormatterFn ) => string; diff --git a/public/app/features/variables/state/onTimeRangeUpdated.test.ts b/public/app/features/variables/state/onTimeRangeUpdated.test.ts index 417c33f6c7f..e7e24bac26b 100644 --- a/public/app/features/variables/state/onTimeRangeUpdated.test.ts +++ b/public/app/features/variables/state/onTimeRangeUpdated.test.ts @@ -6,6 +6,7 @@ import { appEvents } from '../../../core/core'; import { notifyApp } from '../../../core/reducers/appNotification'; import { DashboardState } from '../../../types'; import { DashboardModel } from '../../dashboard/state'; +import { createDashboardModelFixture } from '../../dashboard/state/__fixtures__/dashboardFixtures'; import { TemplateSrv } from '../../templating/template_srv'; import { variableAdapters } from '../adapters'; import { createConstantVariableAdapter } from '../constant/adapter'; @@ -226,5 +227,5 @@ describe('when onTimeRangeUpdated is dispatched', () => { }); function getDashboardModel(): DashboardModel { - return new DashboardModel({ schemaVersion: 9999 }); // ignore any schema migrations + return createDashboardModelFixture({ schemaVersion: 9999 }); // ignore any schema migrations } diff --git a/public/app/features/variables/state/templateVarsChangedInUrl.test.ts b/public/app/features/variables/state/templateVarsChangedInUrl.test.ts index 31c47aa710b..036a4eaa4c7 100644 --- a/public/app/features/variables/state/templateVarsChangedInUrl.test.ts +++ b/public/app/features/variables/state/templateVarsChangedInUrl.test.ts @@ -1,7 +1,8 @@ import { TypedVariableModel } from '@grafana/data'; import { DashboardState, StoreState } from '../../../types'; -import { DashboardModel, PanelModel } from '../../dashboard/state'; +import { PanelModel } from '../../dashboard/state'; +import { createDashboardModelFixture } from '../../dashboard/state/__fixtures__/dashboardFixtures'; import { initialState } from '../../dashboard/state/reducers'; import { variableAdapters } from '../adapters'; import { createConstantVariableAdapter } from '../constant/adapter'; @@ -13,7 +14,7 @@ import { templateVarsChangedInUrl } from './actions'; import { getPreloadedState } from './helpers'; import { VariablesState } from './types'; -const dashboardModel = new DashboardModel({}); +const dashboardModel = createDashboardModelFixture({}); variableAdapters.setInit(() => [createCustomVariableAdapter(), createConstantVariableAdapter()]); diff --git a/public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx b/public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx index 0a98f2bd947..df9222c1f2c 100644 --- a/public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx +++ b/public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx @@ -8,6 +8,11 @@ import { mockDataSource, MockDataSourceSrv } from 'app/features/alerting/unified import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { DashboardModel } from 'app/features/dashboard/state'; +import { + createDashboardModelFixture, + createPanelJSONFixture, +} from '../../../features/dashboard/state/__fixtures__/dashboardFixtures'; + import { DashboardQueryEditor } from './DashboardQueryEditor'; import { SHARED_DASHBOARD_QUERY } from './types'; @@ -42,21 +47,21 @@ describe('DashboardQueryEditor', () => { let mockDashboard: DashboardModel; beforeEach(() => { - mockDashboard = new DashboardModel({ + mockDashboard = createDashboardModelFixture({ panels: [ - { + createPanelJSONFixture({ targets: [], type: 'timeseries', id: 1, title: 'My first panel', - }, - { + }), + createPanelJSONFixture({ targets: [], id: 2, type: 'timeseries', title: 'Another panel', - }, - { + }), + createPanelJSONFixture({ datasource: { uid: SHARED_DASHBOARD_QUERY, }, @@ -64,7 +69,7 @@ describe('DashboardQueryEditor', () => { id: 3, type: 'timeseries', title: 'A dashboard query panel', - }, + }), ], }); jest.spyOn(getDashboardSrv(), 'getCurrent').mockImplementation(() => mockDashboard); diff --git a/public/app/plugins/panel/graph/specs/graph.test.ts b/public/app/plugins/panel/graph/specs/graph.test.ts index cdceee1108f..07e9a74109a 100644 --- a/public/app/plugins/panel/graph/specs/graph.test.ts +++ b/public/app/plugins/panel/graph/specs/graph.test.ts @@ -6,7 +6,7 @@ import { PanelCtrl } from 'app/angular/panel/panel_ctrl'; import config from 'app/core/config'; import TimeSeries from 'app/core/time_series2'; -import { DashboardModel } from '../../../../features/dashboard/state'; +import { createDashboardModelFixture } from '../../../../features/dashboard/state/__fixtures__/dashboardFixtures'; import { graphDirective, GraphElement } from '../graph'; import { GraphCtrl } from '../module'; @@ -1337,7 +1337,7 @@ describe('grafanaGraph', () => { }); function getGraphElement({ canEdit, canMakeEditable }: { canEdit?: boolean; canMakeEditable?: boolean } = {}) { - const dashboard = new DashboardModel({}); + const dashboard = createDashboardModelFixture({}); dashboard.events.on = jest.fn(); dashboard.meta.canEdit = canEdit; dashboard.meta.canMakeEditable = canMakeEditable; diff --git a/public/app/types/dashboard.ts b/public/app/types/dashboard.ts index 2b393626726..26c5cc08dde 100644 --- a/public/app/types/dashboard.ts +++ b/public/app/types/dashboard.ts @@ -1,6 +1,6 @@ import { DataQuery } from '@grafana/data'; +import { Dashboard } from '@grafana/schema'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; -import { VariableModel } from 'app/features/variables/types'; import { DashboardAcl } from './acl'; @@ -60,12 +60,10 @@ export interface AnnotationsPermissions { organization: AnnotationActions; } -export interface DashboardDataDTO { +// FIXME: This should not override Dashboard types +export interface DashboardDataDTO extends Dashboard { title: string; uid: string; - templating: { - list: VariableModel[]; - }; panels?: any[]; }