diff --git a/src/infra/src/table/migration/m20250107_160900_delete_bad_dashboards.rs b/src/infra/src/table/migration/m20250107_160900_delete_bad_dashboards.rs
index dd15577c45..182b9d22da 100644
--- a/src/infra/src/table/migration/m20250107_160900_delete_bad_dashboards.rs
+++ b/src/infra/src/table/migration/m20250107_160900_delete_bad_dashboards.rs
@@ -13,13 +13,14 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-//! Removes records from the dashboards table whose JSON data column cannot be
-//! parsed into a dashboard.
+//! Migration stub for m20250107_160900_delete_bad_dashboards.
+//!
+//! This migration was originally intended to remove dashboards with unparseable
+//! JSON data, but was found to incorrectly delete valid dashboards with certain
+//! label/legend position configurations. It has been disabled since dashboard
+//! schema migrations are handled by the UI layer. The migration remains in the
+//! sequence to maintain migration ordering consistency.
-use chrono::{DateTime, FixedOffset};
-use sea_orm::{
- ActiveModelTrait, EntityTrait, IntoActiveModel, PaginatorTrait, QueryOrder, TransactionTrait,
-};
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
@@ -27,25 +28,12 @@ pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
- async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
- let txn = manager.get_connection().begin().await?;
-
- // Migrate pages of 100 records at a time to avoid loading too many
- // records into memory.
- let mut pages = sea_orm_dashboards::Entity::find()
- .order_by_asc(sea_orm_dashboards::Column::Id)
- .paginate(&txn, 100);
-
- while let Some(dashboards) = pages.fetch_and_next().await? {
- for d in dashboards {
- if !can_parse_data(&d) {
- let active_model = d.into_active_model();
- active_model.delete(&txn).await?;
- }
- }
- }
-
- txn.commit().await?;
+ async fn up(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
+ // This migration was disabled because it incorrectly deleted valid dashboards
+ // that had label/legend position fields that didn't match the v1-v5 schema
+ // definitions. Dashboard schema migrations are now handled by the UI layer.
+ // See PR #9860 for details.
+ // Do nothing
Ok(())
}
@@ -54,1676 +42,3 @@ impl MigrationTrait for Migration {
Ok(())
}
}
-
-/// Returns true if the dashboard records JSON `data` field can be parsed into
-/// v1, v2, v3, v4, or v5 dashboard.
-pub fn can_parse_data(model: &sea_orm_dashboards::Model) -> bool {
- let mut data = model.data.clone();
-
- if let Some(obj) = data.as_object_mut() {
- // The domain model JSON deserialization logic for v1-v5 expects
- // some or all these fields to be present in the JSON even though we
- // store them in DB columns. Therefore we add these values back into
- // the JSON object so that deserializing the JSON can succeed.
- obj.insert("dashboardId".to_owned(), model.dashboard_id.clone().into());
- obj.insert("version".to_owned(), model.version.into());
- obj.insert("owner".to_owned(), model.owner.clone().into());
- obj.insert(
- "role".to_owned(),
- model.role.clone().unwrap_or_default().into(),
- );
- obj.insert("title".to_owned(), model.title.clone().into());
- obj.insert(
- "description".to_owned(),
- model.description.clone().unwrap_or_default().into(),
- );
- }
-
- match model.version {
- 1 => {
- let parsed: Result = serde_json::from_value(data);
- parsed.is_ok()
- }
- 2 => {
- let parsed: Result = serde_json::from_value(data);
- parsed.is_ok()
- }
- 3 => {
- let parsed: Result = serde_json::from_value(data);
- parsed.is_ok()
- }
- 4 => {
- let parsed: Result = serde_json::from_value(data);
- parsed.is_ok()
- }
- 5 => {
- let parsed: Result = serde_json::from_value(data);
- parsed.is_ok()
- }
- _ => false,
- }
-}
-
-// The schemas of tables might change after subsequent migrations. Therefore
-// this migration only references ORM models in private submodules that should
-// remain unchanged rather than ORM models in the `entity` module that will be
-// updated to reflect the latest changes to table schemas.
-
-/// Representation of the dashboards table at the time this migration executes.
-mod sea_orm_dashboards {
- use sea_orm::entity::prelude::*;
-
- #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
- #[sea_orm(table_name = "dashboards")]
- pub struct Model {
- #[sea_orm(primary_key)]
- pub id: String,
- pub dashboard_id: String,
- pub folder_id: String,
- pub owner: String,
- pub role: Option,
- pub title: String,
- #[sea_orm(column_type = "Text", nullable)]
- pub description: Option,
- pub data: Json,
- pub version: i32,
- pub created_at: i64,
- }
-
- // There are relations but they are not important to this migration.
- #[derive(Clone, Debug, EnumIter, DeriveRelation)]
- pub enum Relation {}
-
- impl ActiveModelBehavior for ActiveModel {}
-}
-
-/// Representation of the v1 dashboard inner data schema at the time this
-/// migration executes.
-#[allow(dead_code)]
-mod v1 {
- use chrono::{DateTime, FixedOffset};
- use serde::Deserialize;
-
- use super::{datetime_now, stream::StreamType};
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Dashboard {
- #[serde(default)]
- pub dashboard_id: String,
- pub title: String,
- pub description: String,
- #[serde(default)]
- pub role: String,
- #[serde(default)]
- pub owner: String,
- #[serde(default = "datetime_now")]
- pub created: DateTime,
- #[serde(default)]
- pub panels: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub layouts: Option>,
- pub variables: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Layout {
- pub x: i64,
- pub y: i64,
- pub w: i64,
- pub h: i64,
- pub i: i64,
- pub panel_id: String,
- #[serde(rename = "static")]
- pub is_static: bool,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Panel {
- pub id: String,
- #[serde(rename = "type")]
- pub typ: String,
- pub fields: PanelFields,
- pub config: PanelConfig,
- pub query: String,
- #[serde(default)]
- pub query_type: String,
- pub custom_query: bool,
- }
-
- #[derive(Deserialize)]
- pub struct PanelFields {
- pub stream: String,
- pub stream_type: StreamType,
- pub x: Vec,
- pub y: Vec,
- pub filter: Vec,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct AxisItem {
- pub label: String,
- pub alias: String,
- pub column: String,
- pub color: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub aggregation_function: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "lowercase")]
- pub enum AggregationFunc {
- Count,
- #[serde(rename = "count-distinct")]
- CountDistinct,
- Histogram,
- Sum,
- Min,
- Max,
- Avg,
- Median,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct PanelFilter {
- #[serde(rename = "type")]
- pub typ: String,
- pub values: Vec,
- pub column: String,
- pub operator: Option,
- pub value: Option,
- }
-
- #[derive(Deserialize)]
- pub struct PanelConfig {
- title: String,
- description: String,
- show_legends: bool,
- legends_position: Option,
- promql_legend: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- unit: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- unit_custom: Option,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Variables {
- pub list: Vec,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct VariableList {
- #[serde(rename = "type")]
- pub type_field: String,
- pub name: String,
- pub label: String,
- #[serde(rename = "query_data")]
- pub query_data: Option,
- pub value: Option,
- pub options: Option>,
- }
-
- #[derive(Default, Deserialize)]
- pub struct QueryData {
- pub stream_type: StreamType,
- pub stream: String,
- pub field: String,
- pub max_record_size: Option,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct CustomFieldsOption {
- pub label: String,
- pub value: String,
- }
-}
-
-/// Representation of the v2 dashboard inner data schema at the time this
-/// migration executes.
-#[allow(dead_code)]
-mod v2 {
- use chrono::{DateTime, FixedOffset};
- use serde::Deserialize;
-
- use super::{datetime_now, stream::StreamType};
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Dashboard {
- version: i32,
- #[serde(default)]
- pub dashboard_id: String,
- pub title: String,
- pub description: String,
- #[serde(default)]
- pub role: String,
- #[serde(default)]
- pub owner: String,
- #[serde(default = "datetime_now")]
- pub created: DateTime,
- #[serde(default)]
- pub panels: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub variables: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Layout {
- pub x: i64,
- pub y: i64,
- pub w: i64,
- pub h: i64,
- pub i: i64,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Panel {
- pub id: String,
- #[serde(rename = "type")]
- pub typ: String,
- pub title: String,
- pub description: String,
- pub config: PanelConfig,
- #[serde(default)]
- pub query_type: String,
- pub queries: Vec,
- pub layout: Layout,
- }
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Query {
- pub query: String,
- pub custom_query: bool,
- pub fields: PanelFields,
- pub config: QueryConfig,
- }
-
- #[derive(Deserialize)]
- pub struct PanelFields {
- pub stream: String,
- pub stream_type: StreamType,
- pub x: Vec,
- pub y: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub z: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub latitude: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub longitude: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub weight: Option,
- pub filter: Vec,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct AxisItem {
- pub label: String,
- pub alias: String,
- pub column: String,
- pub color: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub aggregation_function: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub sort_by: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub args: Option>,
- }
-
- #[derive(Deserialize)]
- pub struct AxisArg {
- value: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "lowercase")]
- pub enum AggregationFunc {
- Count,
- #[serde(rename = "count-distinct")]
- CountDistinct,
- Histogram,
- Sum,
- Min,
- Max,
- Avg,
- Median,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct PanelFilter {
- #[serde(rename = "type")]
- pub typ: String,
- pub values: Vec,
- pub column: String,
- pub operator: Option,
- pub value: Option,
- }
-
- #[derive(Deserialize)]
- pub struct PanelConfig {
- show_legends: bool,
- legends_position: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- unit: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- unit_custom: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- decimals: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- axis_width: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- axis_border_show: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- legend_width: Option,
- base_map: Option,
- map_view: Option,
- }
-
- #[derive(Deserialize)]
- pub struct QueryConfig {
- promql_legend: String,
- #[serde(skip_serializing_if = "Option::is_none")]
- layer_type: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- weight_fixed: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- limit: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- min: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- max: Option,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Variables {
- pub list: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub show_dynamic_filters: Option,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct VariableList {
- #[serde(rename = "type")]
- pub type_field: String,
- pub name: String,
- pub label: String,
- #[serde(rename = "query_data")]
- pub query_data: Option,
- pub value: Option,
- pub options: Option>,
- }
-
- #[derive(Default, Deserialize)]
- pub struct QueryData {
- pub stream_type: StreamType,
- pub stream: String,
- pub field: String,
- pub max_record_size: Option,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct CustomFieldsOption {
- pub label: String,
- pub value: String,
- }
-
- #[derive(Deserialize)]
- pub struct BaseMap {
- #[serde(rename = "type")]
- pub type_field: Option,
- }
-
- #[derive(Deserialize)]
- pub struct MapView {
- pub zoom: f64,
- pub lat: f64,
- pub lng: f64,
- }
-
- #[derive(Deserialize)]
- pub struct LegendWidth {
- #[serde(skip_serializing_if = "Option::is_none")]
- pub value: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub unit: Option,
- }
-}
-
-/// Representation of the v3 dashboard inner data schema at the time this
-/// migration executes.
-#[allow(dead_code)]
-mod v3 {
- use chrono::{DateTime, FixedOffset};
- use serde::Deserialize;
-
- use super::{datetime_now, stream::StreamType};
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Dashboard {
- version: i32,
- #[serde(default)]
- pub dashboard_id: String,
- pub title: String,
- pub description: String,
- #[serde(default)]
- pub role: String,
- #[serde(default)]
- pub owner: String,
- #[serde(default = "datetime_now")]
- pub created: DateTime,
- #[serde(default)]
- pub tabs: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub variables: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub default_datetime_duration: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Layout {
- pub x: i64,
- pub y: i64,
- pub w: i64,
- pub h: i64,
- pub i: i64,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Tab {
- pub tab_id: String,
- pub name: String,
- #[serde(default)]
- pub panels: Vec,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Panel {
- pub id: String,
- #[serde(rename = "type")]
- pub typ: String,
- pub title: String,
- pub description: String,
- pub config: PanelConfig,
- #[serde(default)]
- pub query_type: String,
- pub queries: Vec,
- pub layout: Layout,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub html_content: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub markdown_content: Option,
- }
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Query {
- pub query: Option,
- pub custom_query: bool,
- pub fields: PanelFields,
- pub config: QueryConfig,
- }
-
- #[derive(Deserialize)]
- pub struct PanelFields {
- pub stream: String,
- pub stream_type: StreamType,
- pub x: Vec,
- pub y: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub z: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub latitude: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub longitude: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub weight: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub source: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub target: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub value: Option,
- pub filter: Vec,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct AxisItem {
- pub label: String,
- pub alias: String,
- pub column: String,
- pub color: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub aggregation_function: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub sort_by: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub args: Option>,
- }
-
- #[derive(Deserialize)]
- pub struct AxisArg {
- value: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "lowercase")]
- pub enum AggregationFunc {
- Count,
- #[serde(rename = "count-distinct")]
- CountDistinct,
- Histogram,
- Sum,
- Min,
- Max,
- Avg,
- Median,
- P50,
- P90,
- P95,
- P99,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct PanelFilter {
- #[serde(rename = "type")]
- pub typ: String,
- pub values: Vec,
- pub column: String,
- pub operator: Option,
- pub value: Option,
- }
-
- #[derive(Deserialize)]
- pub struct PanelConfig {
- show_legends: bool,
- legends_position: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- unit: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- unit_custom: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- decimals: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- axis_width: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- axis_border_show: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- legend_width: Option,
- base_map: Option,
- map_view: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- map_symbol_style: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- drilldown: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- connect_nulls: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- no_value_replacement: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- wrap_table_cells: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct DrillDown {
- #[serde(skip_serializing_if = "Option::is_none")]
- name: Option,
- #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
- type_field: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- target_blank: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- find_by: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- data: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct DrillDownData {
- #[serde(skip_serializing_if = "Option::is_none")]
- url: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- folder: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- dashboard: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- tab: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pass_all_variables: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- variables: Option>,
- }
-
- #[derive(Deserialize)]
- pub struct DrillDownVariables {
- name: Option,
- value: Option,
- }
-
- #[derive(Deserialize)]
- pub struct QueryConfig {
- promql_legend: String,
- #[serde(skip_serializing_if = "Option::is_none")]
- layer_type: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- weight_fixed: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- limit: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- min: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- max: Option,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Variables {
- pub list: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub show_dynamic_filters: Option,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct DateTimeOptions {
- #[serde(rename = "type")]
- pub typee: String,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub relative_time_period: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub start_time: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub end_time: Option,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct VariableList {
- #[serde(rename = "type")]
- pub type_field: String,
- pub name: String,
- pub label: String,
- #[serde(rename = "query_data")]
- pub query_data: Option,
- pub value: Option,
- pub options: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub multi_select: Option,
- }
-
- #[derive(Default, Deserialize)]
- pub struct QueryData {
- pub stream_type: StreamType,
- pub stream: String,
- pub field: String,
- pub max_record_size: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub filter: Option>,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Filters {
- pub name: Option,
- pub operator: Option,
- pub value: String,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct CustomFieldsOption {
- pub label: String,
- pub value: String,
- }
-
- #[derive(Deserialize)]
- pub struct BaseMap {
- #[serde(rename = "type")]
- pub type_field: Option,
- }
-
- #[derive(Deserialize)]
- pub struct MapView {
- pub zoom: f64,
- pub lat: f64,
- pub lng: f64,
- }
-
- #[derive(Deserialize)]
- pub struct MapSymbolStyle {
- pub size: String,
- pub size_by_value: Option,
- pub size_fixed: f64,
- }
-
- #[derive(Deserialize)]
- pub struct SizeByValue {
- pub min: f64,
- pub max: f64,
- }
-
- #[derive(Deserialize)]
- pub struct LegendWidth {
- #[serde(skip_serializing_if = "Option::is_none")]
- pub value: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub unit: Option,
- }
-}
-
-/// Representation of the v4 dashboard inner data schema at the time this
-/// migration executes.
-#[allow(dead_code)]
-mod v4 {
- use chrono::{DateTime, FixedOffset};
- use serde::Deserialize;
-
- use super::{datetime_now, stream::StreamType};
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Dashboard {
- version: i32,
- #[serde(default)]
- pub dashboard_id: String,
- pub title: String,
- pub description: String,
- #[serde(default)]
- pub role: String,
- #[serde(default)]
- pub owner: String,
- #[serde(default = "datetime_now")]
- pub created: DateTime,
- #[serde(default)]
- pub tabs: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub variables: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub default_datetime_duration: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Layout {
- pub x: i64,
- pub y: i64,
- pub w: i64,
- pub h: i64,
- pub i: i64,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Tab {
- pub tab_id: String,
- pub name: String,
- #[serde(default)]
- pub panels: Vec,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Panel {
- pub id: String,
- #[serde(rename = "type")]
- pub typ: String,
- pub title: String,
- pub description: String,
- pub config: PanelConfig,
- #[serde(default)]
- pub query_type: String,
- pub queries: Vec,
- pub layout: Layout,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub html_content: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub markdown_content: Option,
- }
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Query {
- pub query: Option,
- pub vrl_function_query: Option,
- pub custom_query: bool,
- pub fields: PanelFields,
- pub config: QueryConfig,
- }
-
- #[derive(Deserialize)]
- pub struct PanelFields {
- pub stream: String,
- pub stream_type: StreamType,
- pub x: Vec,
- pub y: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub z: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub breakdown: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub latitude: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub longitude: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub weight: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub source: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub target: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub value: Option,
- pub filter: Vec,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct AxisItem {
- pub label: String,
- pub alias: String,
- pub column: String,
- pub color: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub aggregation_function: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub sort_by: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub args: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub is_derived: Option,
- }
-
- #[derive(Deserialize)]
- pub struct AxisArg {
- value: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "lowercase")]
- pub enum AggregationFunc {
- Count,
- #[serde(rename = "count-distinct")]
- CountDistinct,
- Histogram,
- Sum,
- Min,
- Max,
- Avg,
- Median,
- P50,
- P90,
- P95,
- P99,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct PanelFilter {
- #[serde(rename = "type")]
- pub typ: String,
- pub values: Vec,
- pub column: String,
- pub operator: Option,
- pub value: Option,
- }
-
- #[derive(Deserialize)]
- pub struct PanelConfig {
- show_legends: bool,
- legends_position: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- unit: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- unit_custom: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- decimals: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- top_results: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- top_results_others: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- axis_width: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- axis_border_show: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- legend_width: Option,
- base_map: Option,
- map_view: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- map_symbol_style: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- drilldown: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- mark_line: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- connect_nulls: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- no_value_replacement: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- wrap_table_cells: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct DrillDown {
- #[serde(skip_serializing_if = "Option::is_none")]
- name: Option,
- #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
- type_field: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- target_blank: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- find_by: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- data: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct MarkLine {
- #[serde(skip_serializing_if = "Option::is_none")]
- name: Option,
- #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
- typee: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- value: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct DrillDownData {
- #[serde(skip_serializing_if = "Option::is_none")]
- url: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- folder: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- dashboard: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- tab: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pass_all_variables: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- variables: Option>,
- }
-
- #[derive(Deserialize)]
- pub struct DrillDownVariables {
- name: Option,
- value: Option,
- }
-
- #[derive(Deserialize)]
- pub struct QueryConfig {
- promql_legend: String,
- #[serde(skip_serializing_if = "Option::is_none")]
- layer_type: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- weight_fixed: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- limit: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- min: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- max: Option,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Variables {
- pub list: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub show_dynamic_filters: Option,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct DateTimeOptions {
- #[serde(rename = "type")]
- pub typee: String,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub relative_time_period: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub start_time: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub end_time: Option,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct VariableList {
- #[serde(rename = "type")]
- pub type_field: String,
- pub name: String,
- pub label: String,
- #[serde(rename = "query_data")]
- pub query_data: Option,
- pub value: Option,
- pub options: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub multi_select: Option,
- }
-
- #[derive(Default, Deserialize)]
- pub struct QueryData {
- pub stream_type: StreamType,
- pub stream: String,
- pub field: String,
- pub max_record_size: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub filter: Option>,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Filters {
- pub name: Option,
- pub operator: Option,
- pub value: String,
- }
-
- #[derive(Default, Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct CustomFieldsOption {
- pub label: String,
- pub value: String,
- }
-
- #[derive(Deserialize)]
- pub struct BaseMap {
- #[serde(rename = "type")]
- pub type_field: Option,
- }
-
- #[derive(Deserialize)]
- pub struct MapView {
- pub zoom: f64,
- pub lat: f64,
- pub lng: f64,
- }
-
- #[derive(Deserialize)]
- pub struct MapSymbolStyle {
- pub size: String,
- pub size_by_value: Option,
- pub size_fixed: f64,
- }
-
- #[derive(Deserialize)]
- pub struct SizeByValue {
- pub min: f64,
- pub max: f64,
- }
-
- #[derive(Deserialize)]
- pub struct LegendWidth {
- #[serde(skip_serializing_if = "Option::is_none")]
- pub value: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub unit: Option,
- }
-}
-
-/// Representation of the v5 dashboard inner data schema at the time this
-/// migration executes.
-#[allow(dead_code)]
-mod v5 {
- use chrono::{DateTime, FixedOffset};
- use serde::Deserialize;
-
- use super::{datetime_now, stream::StreamType};
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Dashboard {
- version: i32,
- #[serde(default)]
- pub dashboard_id: String,
- pub title: String,
- pub description: String,
- #[serde(default)]
- pub role: String,
- #[serde(default)]
- pub owner: String,
- #[serde(default = "datetime_now")]
- pub created: DateTime,
- #[serde(default)]
- pub tabs: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub variables: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub default_datetime_duration: Option,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Layout {
- pub x: i64,
- pub y: i64,
- pub w: i64,
- pub h: i64,
- pub i: i64,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Tab {
- pub tab_id: String,
- pub name: String,
- #[serde(default)]
- pub panels: Vec,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Panel {
- pub id: String,
- #[serde(rename = "type")]
- pub typ: String,
- pub title: String,
- pub description: String,
- pub config: PanelConfig,
- #[serde(default)]
- pub query_type: String,
- pub queries: Vec,
- pub layout: Layout,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub html_content: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub markdown_content: Option,
- }
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct Query {
- pub query: Option,
- pub vrl_function_query: Option,
- pub custom_query: bool,
- pub fields: PanelFields,
- pub config: QueryConfig,
- }
-
- #[derive(Deserialize)]
- pub struct PanelFields {
- pub stream: String,
- pub stream_type: StreamType,
- pub x: Vec,
- pub y: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub z: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub breakdown: Option>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub latitude: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub longitude: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub weight: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub name: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub value_for_maps: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub source: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub target: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub value: Option,
- pub filter: PanelFilter,
- }
-
- #[derive(Deserialize)]
- #[serde(rename_all = "camelCase")]
- pub struct AxisItem {
- pub label: String,
- pub alias: String,
- pub column: String,
- pub color: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub aggregation_function: Option