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:
parent
e46f364920
commit
e289762604
|
@ -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",
|
||||
|
|
|
@ -143,3 +143,4 @@ tonic-build = { version = "0.8", features = ["prost"] }
|
|||
|
||||
[dev-dependencies]
|
||||
datafusion-expr = "20.0"
|
||||
expect-test = "1.4"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -202,7 +202,7 @@ export default defineComponent({
|
|||
callDashboard = dashboardService.create(
|
||||
this.store.state.selectedOrganization.identifier,
|
||||
baseObj.dashboardId,
|
||||
JSON.stringify(JSON.stringify(baseObj))
|
||||
baseObj
|
||||
);
|
||||
}
|
||||
callDashboard
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue