pinakes-ui: fix plugin page data loading; add as_json helper
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I6d80eff06e9ca46f916e643d5d8bb6c86a6a6964
This commit is contained in:
parent
1acff0227c
commit
e55fd5cc98
1 changed files with 142 additions and 2 deletions
|
|
@ -10,7 +10,7 @@ use pinakes_plugin_api::{DataSource, HttpMethod};
|
|||
use crate::client::ApiClient;
|
||||
|
||||
/// Cached data for a plugin page
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct PluginPageData {
|
||||
data: HashMap<String, serde_json::Value>,
|
||||
loading: HashMap<String, bool>,
|
||||
|
|
@ -62,6 +62,19 @@ impl PluginPageData {
|
|||
self.errors.insert(source, error);
|
||||
}
|
||||
|
||||
/// Convert all resolved data to a single JSON object for expression
|
||||
/// evaluation
|
||||
#[must_use]
|
||||
pub fn as_json(&self) -> serde_json::Value {
|
||||
serde_json::Value::Object(
|
||||
self
|
||||
.data
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Clear all data
|
||||
pub fn clear(&mut self) {
|
||||
self.data.clear();
|
||||
|
|
@ -114,7 +127,18 @@ pub async fn fetch_page_data(
|
|||
) -> Result<HashMap<String, serde_json::Value>, String> {
|
||||
let mut results = HashMap::new();
|
||||
|
||||
for (name, source) in data_sources {
|
||||
// Process non-Transform sources first so Transform sources can reference them
|
||||
let mut ordered: Vec<(&String, &DataSource)> = data_sources
|
||||
.iter()
|
||||
.filter(|(_, s)| !matches!(s, DataSource::Transform { .. }))
|
||||
.collect();
|
||||
ordered.extend(
|
||||
data_sources
|
||||
.iter()
|
||||
.filter(|(_, s)| matches!(s, DataSource::Transform { .. })),
|
||||
);
|
||||
|
||||
for (name, source) in ordered {
|
||||
let value = match source {
|
||||
DataSource::Endpoint { path, method, .. } => {
|
||||
// Fetch from endpoint (ignoring params, poll_interval, transform for
|
||||
|
|
@ -168,11 +192,13 @@ pub fn use_plugin_data(
|
|||
match fetch_page_data(&client.read(), &sources).await {
|
||||
Ok(results) => {
|
||||
for (name, value) in results {
|
||||
data.write().set_loading(&name, false);
|
||||
data.write().set_data(name, value);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
for name in sources.keys() {
|
||||
data.write().set_loading(name, false);
|
||||
data.write().set_error(name.clone(), e.clone());
|
||||
}
|
||||
},
|
||||
|
|
@ -270,4 +296,118 @@ mod tests {
|
|||
let data = serde_json::json!({"foo": "bar"});
|
||||
assert!(get_json_path(&data, "nonexistent").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_json_path_array_out_of_bounds() {
|
||||
let data = serde_json::json!({"items": ["a"]});
|
||||
assert!(get_json_path(&data, "items.5").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_json_path_non_array_index() {
|
||||
let data = serde_json::json!({"foo": "bar"});
|
||||
assert!(get_json_path(&data, "foo.0").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as_json_empty() {
|
||||
let data = PluginPageData::default();
|
||||
assert_eq!(data.as_json(), serde_json::json!({}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as_json_with_data() {
|
||||
let mut data = PluginPageData::default();
|
||||
data.set_data("users".to_string(), serde_json::json!([{"id": 1}]));
|
||||
data.set_data("count".to_string(), serde_json::json!(42));
|
||||
let json = data.as_json();
|
||||
assert_eq!(json["users"], serde_json::json!([{"id": 1}]));
|
||||
assert_eq!(json["count"], serde_json::json!(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_loading_true_clears_error() {
|
||||
let mut data = PluginPageData::default();
|
||||
data.set_error("src".to_string(), "oops".to_string());
|
||||
assert!(data.error("src").is_some());
|
||||
data.set_loading("src", true);
|
||||
assert!(data.error("src").is_none());
|
||||
assert!(data.is_loading("src"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_loading_false_removes_flag() {
|
||||
let mut data = PluginPageData::default();
|
||||
data.set_loading("src", true);
|
||||
assert!(data.is_loading("src"));
|
||||
data.set_loading("src", false);
|
||||
assert!(!data.is_loading("src"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear_resets_all_state() {
|
||||
let mut data = PluginPageData::default();
|
||||
data.set_data("x".to_string(), serde_json::json!(1));
|
||||
data.set_loading("x", true);
|
||||
data.set_error("y".to_string(), "err".to_string());
|
||||
data.clear();
|
||||
assert!(!data.has_data("x"));
|
||||
assert!(!data.is_loading("x"));
|
||||
assert!(data.error("y").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_partial_eq() {
|
||||
let mut a = PluginPageData::default();
|
||||
let mut b = PluginPageData::default();
|
||||
assert_eq!(a, b);
|
||||
a.set_data("k".to_string(), serde_json::json!(1));
|
||||
assert_ne!(a, b);
|
||||
b.set_data("k".to_string(), serde_json::json!(1));
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_fetch_page_data_static_only() {
|
||||
use pinakes_plugin_api::DataSource;
|
||||
|
||||
use crate::client::ApiClient;
|
||||
|
||||
let client = ApiClient::default();
|
||||
let mut sources = HashMap::new();
|
||||
sources.insert("nums".to_string(), DataSource::Static {
|
||||
value: serde_json::json!([1, 2, 3]),
|
||||
});
|
||||
sources.insert("flag".to_string(), DataSource::Static {
|
||||
value: serde_json::json!(true),
|
||||
});
|
||||
|
||||
let results = super::fetch_page_data(&client, &sources).await.unwrap();
|
||||
assert_eq!(results["nums"], serde_json::json!([1, 2, 3]));
|
||||
assert_eq!(results["flag"], serde_json::json!(true));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_fetch_page_data_transform_after_static() {
|
||||
use pinakes_plugin_api::{DataSource, Expression};
|
||||
|
||||
use crate::client::ApiClient;
|
||||
|
||||
let client = ApiClient::default();
|
||||
let mut sources = HashMap::new();
|
||||
// Insert Transform before Static in the map to test ordering
|
||||
sources.insert("derived".to_string(), DataSource::Transform {
|
||||
source_name: "raw".to_string(),
|
||||
expression: Expression::Literal(serde_json::Value::Null),
|
||||
});
|
||||
sources.insert("raw".to_string(), DataSource::Static {
|
||||
value: serde_json::json!({"ok": true}),
|
||||
});
|
||||
|
||||
let results = super::fetch_page_data(&client, &sources).await.unwrap();
|
||||
// raw must have been processed before derived
|
||||
assert_eq!(results["raw"], serde_json::json!({"ok": true}));
|
||||
// derived gets source_data from raw (transform is identity for now)
|
||||
assert_eq!(results["derived"], serde_json::json!({"ok": true}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue