fix: typed dashboard structs (#466)

* WIP: introduce `DashboardXxx`; use it for `save_dashboard`

* service::db::dashboard::list: handle errors, don't unwrap

* [WIP] Update `service::db::dashboard::list`

* XXX The code compiles, but the Dashboards page shows nothing

* web: repair 'dashboards' page

Don't `JSON.parse` backend's response, it's JSON already.

* [WIP] Repair the "view dashboard" page

Saving panel(s) is still broken.

* meta::dashboards.rs: define `Panel` struct

* Fix `Panel` struct definition

* Use `crate::common::json` instead of `serde_json`

* remove json stringify for the dashboard

* Fixed dashboards

* WIP: introduce `DashboardXxx`; use it for `save_dashboard`

* service::db::dashboard::list: handle errors, don't unwrap

* [WIP] Update `service::db::dashboard::list`

* XXX The code compiles, but the Dashboards page shows nothing

* web: repair 'dashboards' page

Don't `JSON.parse` backend's response, it's JSON already.

* [WIP] Repair the "view dashboard" page

Saving panel(s) is still broken.

* meta::dashboards.rs: define `Panel` struct

* Fix `Panel` struct definition

* Use `crate::common::json` instead of `serde_json`

* remove json stringify for the dashboard

* Fixed dashboards

* Added all aggregate functions

* FIXED TESTS

* fixed integration tests

* Updated coverage threshold

---------

Co-authored-by: ktx-keshavi <keshavi.bhalani@kiara.tech>
Co-authored-by: oasisk <ashish.j.kolhe@gmail.com>
This commit is contained in:
Valeriy V. Vorotyntsev 2023-03-31 14:33:34 +03:00 committed by GitHub
parent e46f364920
commit e289762604
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 334 additions and 129 deletions

17
Cargo.lock generated
View File

@ -1862,6 +1862,12 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "dissimilar"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "210ec60ae7d710bed8683e333e9d2855a8a56a3e9892b38bad3bb0d4d29b0d5e"
[[package]]
name = "doc-comment"
version = "0.3.3"
@ -1959,6 +1965,16 @@ dependencies = [
"tower-service",
]
[[package]]
name = "expect-test"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3"
dependencies = [
"dissimilar",
"once_cell",
]
[[package]]
name = "extend"
version = "0.1.2"
@ -5255,6 +5271,7 @@ dependencies = [
"dotenvy",
"env_logger",
"etcd-client",
"expect-test",
"flatten-json-object",
"futures",
"get_if_addrs",

View File

@ -143,3 +143,4 @@ tonic-build = { version = "0.8", features = ["prost"] }
[dev-dependencies]
datafusion-expr = "20.0"
expect-test = "1.4"

View File

@ -27,9 +27,9 @@ echo "region_cov $region_cov"
# enable threshold
#COVERAGE_THRESHOLD=80
FUNC_COV_THRESHOLD=60
LINE_COV_THRESHOLD=53
REGION_COV_THRESHOLD=40
FUNC_COV_THRESHOLD=68
LINE_COV_THRESHOLD=57
REGION_COV_THRESHOLD=45
# clean up
# find ./target -name llvm-cov-target -type d|xargs rm -fR

View File

@ -15,15 +15,15 @@
use actix_web::{delete, get, post, web, HttpResponse, Responder};
use std::io::Error;
use crate::service::dashboards;
use crate::{meta::dashboards::Dashboard, service::dashboards};
#[post("/{org_id}/dashboards/{name}")]
pub async fn save_dashboard(
path: web::Path<(String, String)>,
details: web::Json<String>,
details: web::Json<Dashboard>,
) -> Result<HttpResponse, Error> {
let (org_id, name) = path.into_inner();
dashboards::save_dashboard(&org_id, &name, &details).await
dashboards::save_dashboard(&org_id, &name, &details.into_inner()).await
}
#[get("/{org_id}/dashboards")]

View File

@ -12,45 +12,259 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use chrono::{DateTime, FixedOffset};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Dashboard {
pub name: String,
pub details: String,
}
use super::StreamType;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DashboardList {
pub list: Vec<Dashboard>,
pub list: Vec<NamedDashboard>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NamedDashboard {
// XXX-REVIEW: Do we need this field (and the encompassing struct) at all?
// AFAICS, name equals `Dashboard::dashboard_id`, so there's a duplication.
pub name: String,
pub details: Dashboard,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Dashboard {
pub title: String,
pub dashboard_id: String,
pub description: String,
pub role: String,
pub owner: String,
pub created: DateTime<FixedOffset>,
pub panels: Vec<Panel>,
#[serde(skip_serializing_if = "Option::is_none")]
pub layouts: Option<Vec<Layout>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Layout {
pub x: i64,
pub y: i64,
pub w: i64,
pub h: i64,
pub i: i64,
#[serde(rename = "panelId")]
pub panel_id: String,
#[serde(rename = "static")]
pub is_static: bool,
}
// XXX-TODO: Move `Panel` and the associate structs into a separate module.
// `meta::dashboard::panel` perhaps?
#[derive(Debug, Clone, Serialize, 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,
pub custom_query: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PanelFields {
pub stream: String,
pub stream_type: StreamType,
pub x: Vec<AxisItem>,
pub y: Vec<AxisItem>,
pub filter: Vec<PanelFilter>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AxisItem {
pub label: String,
pub alias: String,
pub column: String,
pub color: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub aggregation_function: Option<AggregationFunc>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AggregationFunc {
Count,
Histogram,
Sum,
Min,
Max,
Avg,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PanelFilter {
#[serde(rename = "type")]
pub typ: String, // HACK: use enum
pub values: Vec<()>, // XXX-FIXME
pub column: String, // HACK: use enum
pub operator: String, // HACK: use enum
pub value: Option<String>, // XXX-REVIEW
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PanelConfig {
title: String,
description: String,
show_legends: bool,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::json;
use expect_test::expect;
#[test]
fn test_dashboard() {
let dashboard = Dashboard {
name: "test".to_string(),
details: "test".to_string(),
};
assert_eq!(dashboard.name, "test");
assert_eq!(dashboard.details, "test");
fn test_dashboard_defs() {
let dashboard: Dashboard = json::from_str(r##"{
"title": "b2",
"dashboardId": "1501078512",
"description": "desc2",
"role": "",
"owner": "root@example.com",
"created": "2023-03-30T07:49:41.744+00:00",
"panels": [
{
"id": "Panel_ID7857010",
"type": "bar",
"fields": {
"stream": "default",
"stream_type": "logs",
"x": [
{
"label": "Timestamp",
"alias": "x_axis_1",
"column": "_timestamp",
"color": null,
"aggregationFunction": "histogram"
}
],
"y": [
{
"label": "Kubernetes Host",
"alias": "y_axis_1",
"column": "kubernetes_host",
"color": "#5960b2",
"aggregationFunction": "count"
}
],
"filter": [
{
"type": "condition",
"values": [],
"column": "method",
"operator": "Is Not Null",
"value": null
}
]
},
"config": {
"title": "p5",
"description": "sample config blah blah blah",
"show_legends": true
},
"query": "SELECT histogram(_timestamp) as \"x_axis_1\", count(kubernetes_host) as \"y_axis_1\" FROM \"default\" WHERE method IS NOT NULL GROUP BY \"x_axis_1\" ORDER BY \"x_axis_1\"",
"customQuery": false
}
],
"layouts": [
{
"x": 0,
"y": 0,
"w": 12,
"h": 13,
"i": 1,
"panelId": "Panel_ID7857010",
"static": false
}
]
}"##).unwrap();
let dashboard_str = json::to_string(&dashboard.clone()).unwrap();
let dashboard2: Dashboard = json::from_str(&dashboard_str).unwrap();
assert_eq!(dashboard.name, dashboard2.name);
assert_eq!(format!("{:?}", dashboard), format!("{:?}", dashboard2));
let dslist = DashboardList {
list: vec![dashboard.clone()],
};
assert!(!dslist.list.is_empty());
let dslist_str = json::to_string(&dslist.clone()).unwrap();
let dslist2: DashboardList = json::from_str(&dslist_str).unwrap();
assert_eq!(dslist.list.len(), dslist2.list.len());
assert_eq!(format!("{:?}", dslist), format!("{:?}", dslist2));
expect![[r##"
Dashboard {
title: "b2",
dashboard_id: "1501078512",
description: "desc2",
role: "",
owner: "root@example.com",
created: 2023-03-30T07:49:41.744+00:00,
panels: [
Panel {
id: "Panel_ID7857010",
typ: "bar",
fields: PanelFields {
stream: "default",
stream_type: Logs,
x: [
AxisItem {
label: "Timestamp",
alias: "x_axis_1",
column: "_timestamp",
color: None,
aggregation_function: Some(
Histogram,
),
},
],
y: [
AxisItem {
label: "Kubernetes Host",
alias: "y_axis_1",
column: "kubernetes_host",
color: Some(
"#5960b2",
),
aggregation_function: Some(
Count,
),
},
],
filter: [
PanelFilter {
typ: "condition",
values: [],
column: "method",
operator: "Is Not Null",
value: None,
},
],
},
config: PanelConfig {
title: "p5",
description: "sample config blah blah blah",
show_legends: true,
},
query: "SELECT histogram(_timestamp) as \"x_axis_1\", count(kubernetes_host) as \"y_axis_1\" FROM \"default\" WHERE method IS NOT NULL GROUP BY \"x_axis_1\" ORDER BY \"x_axis_1\"",
custom_query: false,
},
],
layouts: Some(
[
Layout {
x: 0,
y: 0,
w: 12,
h: 13,
i: 1,
panel_id: "Panel_ID7857010",
is_static: false,
},
],
),
}
"##]].assert_debug_eq(&dashboard);
}
}

View File

@ -16,18 +16,17 @@ use actix_web::{
http::{self, StatusCode},
HttpResponse,
};
use std::io::Error;
use std::io;
use tracing::info_span;
use crate::meta::dashboards::DashboardList;
use crate::meta::{self, http::HttpResponse as MetaHttpResponse};
use crate::service::db;
use crate::meta::{self, dashboards::Dashboard, http::HttpResponse as MetaHttpResponse};
use crate::service::db::dashboard;
pub async fn get_dashboard(org_id: &str, name: &str) -> Result<HttpResponse, Error> {
pub async fn get_dashboard(org_id: &str, name: &str) -> Result<HttpResponse, io::Error> {
let loc_span = info_span!("service:dashboards:get");
let _guard = loc_span.enter();
let ret = db::dashboard::get(org_id, name).await;
match ret {
match dashboard::get(org_id, name).await {
Ok(Some(dashboard)) => Ok(HttpResponse::Ok().json(dashboard)),
Ok(None) => Ok(HttpResponse::NotFound().json(MetaHttpResponse::error(
StatusCode::NOT_FOUND.into(),
@ -43,12 +42,12 @@ pub async fn get_dashboard(org_id: &str, name: &str) -> Result<HttpResponse, Err
pub async fn save_dashboard(
org_id: &str,
name: &str,
details: &str,
) -> Result<HttpResponse, Error> {
dashboard: &Dashboard,
) -> Result<HttpResponse, io::Error> {
let loc_span = info_span!("service:dashboards:save");
let _guard = loc_span.enter();
let ret = db::dashboard::set(org_id, name, details).await;
match ret {
match dashboard::set(org_id, name, dashboard).await {
Ok(_) => Ok(HttpResponse::Ok().json(MetaHttpResponse::message(
http::StatusCode::OK.into(),
"Dashboard saved".to_string(),
@ -62,17 +61,21 @@ pub async fn save_dashboard(
}
}
pub async fn list_dashboards(org_id: &str) -> Result<HttpResponse, Error> {
pub async fn list_dashboards(org_id: &str) -> Result<HttpResponse, io::Error> {
let loc_span = info_span!("service:dashboards:list");
let _guard = loc_span.enter();
let list = db::dashboard::list(org_id).await.unwrap();
Ok(HttpResponse::Ok().json(DashboardList { list }))
use meta::dashboards::DashboardList;
Ok(HttpResponse::Ok().json(DashboardList {
list: dashboard::list(org_id).await.unwrap(),
}))
}
pub async fn delete_dashboard(org_id: &str, name: &str) -> Result<HttpResponse, Error> {
pub async fn delete_dashboard(org_id: &str, name: &str) -> Result<HttpResponse, io::Error> {
let loc_span = info_span!("service:dashboards:delete");
let _guard = loc_span.enter();
let result = db::dashboard::delete(org_id, name).await;
let result = dashboard::delete(org_id, name).await;
match result {
Ok(_) => Ok(HttpResponse::Ok().json(MetaHttpResponse::message(
http::StatusCode::OK.into(),

View File

@ -12,25 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use bytes::Bytes;
use anyhow::Context as _;
use crate::meta::dashboards::Dashboard;
use crate::{
common::json,
meta::dashboards::{Dashboard, NamedDashboard},
};
pub async fn get(org_id: &str, name: &str) -> Result<Option<Dashboard>, anyhow::Error> {
pub async fn get(org_id: &str, name: &str) -> Result<Option<NamedDashboard>, anyhow::Error> {
let db = &crate::infra::db::DEFAULT;
let key = format!("/dashboard/{org_id}/{name}");
let ret = db.get(&key).await?;
let details = String::from_utf8(ret.to_vec()).unwrap();
Ok(Some(Dashboard {
let val = db.get(&key).await?;
let details: Dashboard = json::from_slice(&val).with_context(|| {
format!("Failed to deserialize the value for key {key:?} as `Dashboard`")
})?;
Ok(Some(NamedDashboard {
name: name.to_string(),
details,
}))
}
pub async fn set(org_id: &str, name: &str, details: &str) -> Result<(), anyhow::Error> {
pub async fn set(org_id: &str, name: &str, dashboard: &Dashboard) -> Result<(), anyhow::Error> {
let db = &crate::infra::db::DEFAULT;
let key = format!("/dashboard/{org_id}/{name}");
Ok(db.put(&key, Bytes::from(details.to_string())).await?)
Ok(db.put(&key, json::to_vec(dashboard)?.into()).await?)
}
pub async fn delete(org_id: &str, name: &str) -> Result<(), anyhow::Error> {
@ -39,15 +44,21 @@ pub async fn delete(org_id: &str, name: &str) -> Result<(), anyhow::Error> {
Ok(db.delete(&key, false).await?)
}
pub async fn list(org_id: &str) -> Result<Vec<Dashboard>, anyhow::Error> {
pub async fn list(org_id: &str) -> Result<Vec<NamedDashboard>, anyhow::Error> {
let db = &crate::infra::db::DEFAULT;
let key = format!("/dashboard/{org_id}/");
let ret = db.list(&key).await?;
let mut udf_list: Vec<Dashboard> = Vec::new();
for (item_key, item_value) in ret {
let name = item_key.strip_prefix(&key).unwrap().to_string();
let details = String::from_utf8(item_value.to_vec()).unwrap();
udf_list.push(Dashboard { name, details })
}
Ok(udf_list)
let db_key = format!("/dashboard/{org_id}/");
db.list(&db_key)
.await?
.into_iter()
.map(|(k, v)| {
let name = k
.strip_prefix(&db_key)
.expect("BUG: key {k:?} doesn't start with {db_key:?}")
.to_string();
let details: Dashboard = json::from_slice(&v).with_context(|| {
format!("Failed to deserialize the value for key {db_key:?} as `Dashboard`")
})?;
Ok(NamedDashboard { name, details })
})
.collect()
}

View File

@ -761,7 +761,7 @@ mod tests {
}
async fn e2e_post_dashboard() {
let auth = setup();
let body_str = r#""{\"label\" : \"Dashboard1\",\"panels\":[{\"query\": [\"select a as x_axis_chart, b as y_axis_chart from k8s-logs-2022.10.31 group by b\"],\"x_label\": \"\",\"y_label\": \"\",\"title\": \"\",\"type\": \"bar\",\"position\": { \"x_axis\": [1,3],\"y_axis\": [1,1]}}]}""#;
let body_str = r##"{"title":"b2","dashboardId":"1501078512","description":"desc2","role":"","owner":"root@example.com","created":"2023-03-30T07:49:41.744+00:00","panels":[{"id":"Panel_ID7857010","type":"bar","fields":{"stream":"default","stream_type":"logs","x":[{"label":"Timestamp","alias":"x_axis_1","column":"_timestamp","color":null,"aggregationFunction":"histogram"}],"y":[{"label":"Kubernetes Host","alias":"y_axis_1","column":"kubernetes_host","color":"#5960b2","aggregationFunction":"count"}],"filter":[{"type":"condition","values":[],"column":"method","operator":"Is Not Null","value":null}]},"config":{"title":"p5","description":"sample config blah blah blah","show_legends":true},"query":"SELECT histogram(_timestamp) as \"x_axis_1\", count(kubernetes_host) as \"y_axis_1\" FROM \"default\" WHERE method IS NOT NULL GROUP BY \"x_axis_1\" ORDER BY \"x_axis_1\"","customQuery":false}],"layouts":[{"x":0,"y":0,"w":12,"h":13,"i":1,"panelId":"Panel_ID7857010","static":false}]}"##;
let app = test::init_service(
App::new()
.app_data(web::JsonConfig::default().limit(CONFIG.limit.req_json_limit))

View File

@ -202,7 +202,7 @@ export default defineComponent({
callDashboard = dashboardService.create(
this.store.state.selectedOrganization.identifier,
baseObj.dashboardId,
JSON.stringify(JSON.stringify(baseObj))
baseObj
);
}
callDashboard

View File

@ -120,8 +120,6 @@ export const getAllDashboards = async (store: any) => {
return await dashboardService
.list(0, 1000, "name", false, "",store.state.selectedOrganization.identifier)
.then((res) => {
// console.log('dashboard list retriveid');
// console.log(res);
// save to store
store.dispatch("setAllDashboardList", res.data.list);
})
@ -140,7 +138,7 @@ export const addPanel = async (store: any, dashboardId: any, panelData: any) =>
}
const dashboardList = store.state.allDashboardList
let currentDashboard = dashboardList.find((it:any)=>it.name === dashboardId )
currentDashboard = JSON.parse(currentDashboard.details)
currentDashboard = currentDashboard.details
if(!currentDashboard.panels){
currentDashboard.panels = []
}
@ -182,7 +180,7 @@ export const deletePanel = async (store:any, dashboardId:any, panelId:any) => {
// call the update dashboard function
const dashboardList = store.state.allDashboardList
let currentDashboard = dashboardList.find((it:any)=>it.name === dashboardId )
currentDashboard = JSON.parse(currentDashboard.details)
currentDashboard = currentDashboard.details
//remove panel from current dashboard
const panelIndex = currentDashboard.panels.findIndex(
@ -207,7 +205,7 @@ export const updatePanel = async (store:any, dashboardId:any, panelData:any) =>
// call the update dashboard function
const dashboardList = store.state.allDashboardList
let currentDashboard = dashboardList.find((it:any)=>it.name === dashboardId )
currentDashboard = JSON.parse(currentDashboard.details)
currentDashboard = currentDashboard.details
//remove panel from current dashboard
const panelIndex = currentDashboard.panels.findIndex(
@ -225,7 +223,7 @@ export const updateDashboard = async (store: any, org:any, dashboardId:any, curr
.save(
org,
dashboardId,
JSON.stringify(JSON.stringify(currentDashboardData))
currentDashboardData
)
.then(async (res) => {
// update dashboardList
@ -239,8 +237,7 @@ export const getDashboard = async (store: any, dashboardId: any) => {
}
const dashboardList = store.state.allDashboardList
let currentDashboard = dashboardList.find((it:any)=>it.name === dashboardId )
currentDashboard = JSON.parse(currentDashboard.details)
return currentDashboard
return currentDashboard.details
}
export const getPanel = async (store: any, dashboardId: any, panelId: any) => {
@ -249,6 +246,6 @@ export const getPanel = async (store: any, dashboardId: any, panelId: any) => {
}
const dashboardList = store.state.allDashboardList
let currentDashboard = dashboardList.find((it:any)=>it.name === dashboardId )
currentDashboard = JSON.parse(currentDashboard.details)
currentDashboard = currentDashboard.details
return currentDashboard.panels?.find((it: any) => it.id == panelId)
}

View File

@ -232,7 +232,6 @@ export default defineComponent({
showAddDashboardDialog.value = true;
};
const routeToViewD = (row) => {
// console.log("row");
return router.push({
path: "/dashboards/view",
query: { dashboard: row.identifier },
@ -254,27 +253,26 @@ export default defineComponent({
)
.then((res) => {
resultTotal.value = res.data.list.length;
// console.log(res);
store.dispatch("setAllDashboardList", res.data.list);
dismiss();
})
.catch((error) => {
// console.log(error);
console.error(error);
});
};
const dashboards = computed(function () {
const dashboardList = toRaw(store.state.allDashboardList);
return dashboardList.map((data: any, index) => {
const jsonDataOBj = JSON.parse(data.details);
const board = data.details;
return {
"#": index < 9 ? `0${index + 1}` : index + 1,
id: jsonDataOBj.dashboardId,
name: jsonDataOBj.title,
identifier: jsonDataOBj.dashboardId,
description: jsonDataOBj.description,
owner: jsonDataOBj.owner,
created: date.formatDate(jsonDataOBj.created, "YYYY-MM-DDTHH:mm:ssZ"),
id: board.dashboardId,
name: board.title,
identifier: board.dashboardId,
description: board.description,
owner: board.owner,
created: date.formatDate(board.created, "YYYY-MM-DDTHH:mm:ssZ"),
actions: "true",
};
});
@ -380,8 +378,6 @@ export default defineComponent({
(newVal != oldVal || this.dashboards.value == undefined) &&
this.router.currentRoute.value.name == "dashboards"
) {
// console.log("inside if");
this.getDashboards(this.store.state.selectedOrganization.id);
}
},

View File

@ -154,27 +154,6 @@ export default defineComponent({
goBack();
};
// delete dashboard remove the data from database and update store variable and redirect to dashboardList page
// const deleteDashboard = async (dashboardId: String) => {
// await dashboardService
// .delete(store.state.selectedOrganization.identifier, dashboardId)
// .then((res) => {
// const dashboardList = JSON.parse(
// JSON.stringify(toRaw(store.state.allDashboardList))
// );
// const newDashboardList = dashboardList.filter(
// (dashboard) => dashboard.name != dashboardId
// );
// store.dispatch("setAllDashboardList", newDashboardList);
// $q.notify({
// type: "positive",
// message: "Dashboard deleted successfully.",
// timeout: 5000,
// });
// });
// goBack();
// };
const deleteDashboardOnClick = async () => {
await deleteDashboard(route.query.dashboard);
};
@ -189,11 +168,6 @@ export default defineComponent({
currentDashboardData.data
);
// currentDashboardData.data = await getDashboard(
// store,
// dashboardId
// );
$q.notify({
type: "positive",
message: "Dashboard updated successfully.",
@ -202,7 +176,6 @@ export default defineComponent({
};
//add dashboardId
const addNewPanel = (dashboardId: String) => {
return router.push({
@ -215,6 +188,7 @@ export default defineComponent({
addNewPanel(route.query.dashboard);
};
// XXX-RENAMEME: s/list/dashboards/?
let list = computed(function () {
return [toRaw(currentDashboardData.data)];
});
@ -294,14 +268,6 @@ export default defineComponent({
this.draggable = false;
},
async onUpdatePanel(panelDataElementValue: any) {
// console.log(
// "deleting",
// this.$route.query.dashboard,
// panelDataElementValue,
// panelDataElementValue.id
// );
await deletePanel(
this.store,
this.$route.query.dashboard,

View File

@ -174,7 +174,7 @@ export default defineComponent({
);
// console.log("panel data", panelData);
Object.assign(dashboardPanelData.data, panelData);
chartData.value = JSON.parse(JSON.stringify(dashboardPanelData.data));
chartData.value = dashboardPanelData.data
} else {
editMode.value = false;
resetDashboardPanelData();
@ -201,7 +201,7 @@ export default defineComponent({
})
watch(()=> dashboardPanelData.data.type, ()=>{
chartData.value = JSON.parse(JSON.stringify(dashboardPanelData.data));
chartData.value = dashboardPanelData.data
})
const runQuery = () => {
@ -210,7 +210,7 @@ export default defineComponent({
return
}
// copy the data object excluding the reactivity
chartData.value = JSON.parse(JSON.stringify(dashboardPanelData.data));
chartData.value = dashboardPanelData.data
};
const updateDateTime = (value: object) => {
@ -343,7 +343,7 @@ export default defineComponent({
await updatePanel(
store,
dashId,
JSON.parse(JSON.stringify(dashboardPanelData.data))
dashboardPanelData.data
);
} else {
const panelId =
@ -353,7 +353,7 @@ export default defineComponent({
await addPanel(
store,
dashId,
JSON.parse(JSON.stringify(dashboardPanelData.data))
dashboardPanelData.data
);
}