From ee7eeabc4fc62269d4c1e40664dc5a0fb3969760 Mon Sep 17 00:00:00 2001 From: Rusko124 Date: Thu, 26 Feb 2026 12:29:54 +0400 Subject: [PATCH] Add categories field to Edge App manifest --- docs/EdgeApps.md | 8 ++++++++ src/api/version.rs | 4 +++- src/commands/edge_app/app.rs | 16 ++++++++++++---- src/commands/edge_app/manifest.rs | 25 +++++++++++++++++++++++++ src/commands/edge_app/test_utils.rs | 1 + src/commands/edge_app/utils.rs | 5 +++++ 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/docs/EdgeApps.md b/docs/EdgeApps.md index dd8d1ad..237f805 100644 --- a/docs/EdgeApps.md +++ b/docs/EdgeApps.md @@ -24,6 +24,7 @@ Here's a content section for the document: - [Icon](#icon) - [Author](#author) - [Homepage URL](#homepage-url) + - [Categories](#categories) - [Auth](#auth) - [Ready Signal](#ready-signal) - [Settings](#settings) @@ -206,6 +207,9 @@ description: 'Displays the current weather and time' icon: 'https://example.com/some-logo.svg' author: 'Screenly, Inc' homepage_url: 'https://www.screenly.io' +categories: + - Utilities + - Dashboards settings: google_maps_api_key: type: secret @@ -263,6 +267,10 @@ The `author` field specifies the name of the Edge App's creator. The `homepage_url` field is a URL directing to the homepage of the Edge App. +#### Categories + +The `categories` field is an optional list of category names that classify the Edge App (e.g., `Utilities`, `Dashboards`). When omitted, defaults to an empty list. + #### Auth The `auth` field is optional and is used to configure Edge App authentication. It includes the following subfields: diff --git a/src/api/version.rs b/src/api/version.rs index 6972f25..9f9514b 100644 --- a/src/api/version.rs +++ b/src/api/version.rs @@ -17,6 +17,8 @@ pub struct EdgeAppVersion { #[serde(default)] pub homepage_url: Option, #[serde(default)] + pub categories: Vec, + #[serde(default)] pub ready_signal: bool, #[serde(default)] pub revision: u32, @@ -30,7 +32,7 @@ impl Api { let response = commands::get( &self.authentication, &format!( - "v4.1/edge-apps/versions?select=user_version,description,icon,author,homepage_url,revision,ready_signal&app_id=eq.{app_id}&order=revision.desc&limit=1" + "v4.1/edge-apps/versions?select=user_version,description,icon,author,homepage_url,categories,revision,ready_signal&app_id=eq.{app_id}&order=revision.desc&limit=1" ), )?; diff --git a/src/commands/edge_app/app.rs b/src/commands/edge_app/app.rs index 6154163..baf96d0 100644 --- a/src/commands/edge_app/app.rs +++ b/src/commands/edge_app/app.rs @@ -580,6 +580,7 @@ impl EdgeAppCommand { icon: manifest.icon.clone(), author: manifest.author.clone(), homepage_url: manifest.homepage_url.clone(), + categories: manifest.categories.clone(), revision: _version.revision, }), None => Ok(false), @@ -901,7 +902,7 @@ mod tests { ) .query_param( "select", - "user_version,description,icon,author,homepage_url,revision,ready_signal", + "user_version,description,icon,author,homepage_url,categories,revision,ready_signal", ) .query_param("app_id", "eq.01H2QZ6Z8WXWNDC0KQ198XCZEW") .query_param("order", "revision.desc") @@ -913,6 +914,7 @@ mod tests { "icon": "icon", "author": "author", "homepage_url": "homepage_url", + "categories": [], "ready_signal": false, "revision": 7, } @@ -994,6 +996,7 @@ mod tests { "description": "asdf", "icon": "asdf", "homepage_url": "asdfasdf", + "categories": ["Utilities", "Dashboards"], "file_tree": { "index.html": "0a209f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08122086cebd0c365d241e32d5b0972c07aae3a8d6499c2a9471aa85943a35577200021a180a14a94a8fe5ccb19ba61c4c0873d391e987982fbbd31000" }, @@ -1226,7 +1229,7 @@ mod tests { ) .query_param( "select", - "user_version,description,icon,author,homepage_url,revision,ready_signal", + "user_version,description,icon,author,homepage_url,categories,revision,ready_signal", ) .query_param("app_id", "eq.01H2QZ6Z8WXWNDC0KQ198XCZEW") .query_param("order", "revision.desc") @@ -1238,6 +1241,7 @@ mod tests { "icon": "asdf", "author": "asdf", "homepage_url": "asdfasdf", + "categories": ["Utilities", "Dashboards"], "ready_signal": false, "revision": 1 } @@ -1293,7 +1297,7 @@ mod tests { ) .query_param( "select", - "user_version,description,icon,author,homepage_url,revision,ready_signal", + "user_version,description,icon,author,homepage_url,categories,revision,ready_signal", ) .query_param("app_id", "eq.01H2QZ6Z8WXWNDC0KQ198XCZEW") .query_param("order", "revision.desc") @@ -1305,6 +1309,7 @@ mod tests { "icon": "another_icon", "author": "asdf", "homepage_url": "asdfasdf", + "categories": [], "ready_signal": false, "revision": 1, } @@ -1360,7 +1365,7 @@ mod tests { ) .query_param( "select", - "user_version,description,icon,author,homepage_url,revision,ready_signal", + "user_version,description,icon,author,homepage_url,categories,revision,ready_signal", ) .query_param("app_id", "eq.01H2QZ6Z8WXWNDC0KQ198XCZEW") .query_param("order", "revision.desc") @@ -1514,6 +1519,7 @@ mod tests { icon: Some("asdf".to_string()), author: Some("asdf".to_string()), homepage_url: Some("asdfasdf".to_string()), + categories: vec!["Utilities".to_string(), "Dashboards".to_string()], entrypoint: Some(Entrypoint { entrypoint_type: EntrypointType::File, uri: None, @@ -1585,6 +1591,7 @@ mod tests { icon: Some("asdf".to_string()), author: Some("asdf".to_string()), homepage_url: Some("asdfasdf".to_string()), + categories: vec![], entrypoint: None, settings: vec![ Setting { @@ -1700,6 +1707,7 @@ mod tests { icon: Some("asdf".to_string()), author: Some("asdf".to_string()), homepage_url: Some("asdfasdf".to_string()), + categories: vec![], entrypoint: None, settings: vec![ Setting { diff --git a/src/commands/edge_app/manifest.rs b/src/commands/edge_app/manifest.rs index 4a25d26..162f408 100644 --- a/src/commands/edge_app/manifest.rs +++ b/src/commands/edge_app/manifest.rs @@ -85,6 +85,9 @@ pub struct EdgeAppManifest { )] pub homepage_url: Option, + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub categories: Vec, + #[serde( deserialize_with = "deserialize_entrypoint", skip_serializing_if = "Option::is_none", @@ -297,6 +300,8 @@ impl EdgeAppManifest { json!(manifest.ready_signal.unwrap_or(false)), ); + payload.insert("categories", json!(manifest.categories)); + payload } @@ -340,6 +345,7 @@ mod tests { icon: Some("test_icon".to_string()), author: Some("test_author".to_string()), homepage_url: Some("test_url".to_string()), + categories: vec!["Utilities".to_string(), "Dashboards".to_string()], entrypoint: Some(Entrypoint { entrypoint_type: EntrypointType::File, uri: None, @@ -398,6 +404,9 @@ description: test_description icon: test_icon author: test_author homepage_url: test_url +categories: +- Utilities +- Dashboards entrypoint: type: file ready_signal: true @@ -432,6 +441,9 @@ id: test_app user_version: test_version icon: test_icon homepage_url: test_url +categories: +- Utilities +- Dashboards entrypoint: type: file settings: @@ -464,6 +476,9 @@ id: test_app user_version: test_version icon: test_icon homepage_url: test_url +categories: +- Utilities +- Dashboards entrypoint: type: file ready_signal: true @@ -665,6 +680,7 @@ settings: icon: None, author: Some("test_author".to_string()), homepage_url: None, + categories: vec![], entrypoint: Some(Entrypoint { entrypoint_type: EntrypointType::File, uri: None, @@ -894,6 +910,7 @@ settings: icon: Some("test_icon".to_string()), author: Some("test_author".to_string()), homepage_url: Some("test_url".to_string()), + categories: vec!["Utilities".to_string(), "Dashboards".to_string()], entrypoint: Some(Entrypoint { entrypoint_type: EntrypointType::File, uri: None, @@ -921,6 +938,9 @@ description: test_description icon: test_icon author: test_author homepage_url: test_url +categories: +- Utilities +- Dashboards entrypoint: type: file settings: @@ -948,6 +968,7 @@ settings: icon: Some("test_icon".to_string()), author: Some("test_author".to_string()), homepage_url: Some("test_url".to_string()), + categories: vec!["Utilities".to_string(), "Dashboards".to_string()], entrypoint: Some(Entrypoint { entrypoint_type: EntrypointType::File, uri: Some("entrypoint.html".to_string()), @@ -969,6 +990,7 @@ settings: assert_eq!(result["icon"], json!("test_icon")); assert_eq!(result["author"], json!("test_author")); assert_eq!(result["homepage_url"], json!("test_url")); + assert_eq!(result["categories"], json!(["Utilities", "Dashboards"])); assert_eq!(result["entrypoint"], json!("entrypoint.html")); assert_eq!(result["ready_signal"], json!(false)); // Added assertion for ready_signal } @@ -992,6 +1014,7 @@ settings: assert_eq!(result["icon"], json!("test_icon")); assert!(!result.contains_key("author")); assert_eq!(result["homepage_url"], json!("test_url")); + assert_eq!(result["categories"], json!([])); assert!(!result.contains_key("entrypoint")); assert_eq!(result["ready_signal"], json!(false)); // Added assertion for ready_signal } @@ -1006,6 +1029,7 @@ settings: icon: Some("test_icon".to_string()), author: Some("test_author".to_string()), homepage_url: Some("test_url".to_string()), + categories: vec!["Utilities".to_string(), "Dashboards".to_string()], entrypoint: Some(Entrypoint { entrypoint_type: EntrypointType::File, uri: Some("entrypoint.html".to_string()), @@ -1019,6 +1043,7 @@ settings: assert_eq!(result["icon"], json!("test_icon")); assert_eq!(result["author"], json!("test_author")); assert_eq!(result["homepage_url"], json!("test_url")); + assert_eq!(result["categories"], json!(["Utilities", "Dashboards"])); assert_eq!(result["entrypoint"], json!("entrypoint.html")); assert_eq!(result["ready_signal"], json!(true)); // Assert ready_signal is true } diff --git a/src/commands/edge_app/test_utils.rs b/src/commands/edge_app/test_utils.rs index ec8d1ed..4b11f46 100644 --- a/src/commands/edge_app/test_utils.rs +++ b/src/commands/edge_app/test_utils.rs @@ -23,6 +23,7 @@ pub mod tests { icon: Some("asdf".to_string()), author: Some("asdf".to_string()), homepage_url: Some("asdfasdf".to_string()), + categories: vec!["Utilities".to_string(), "Dashboards".to_string()], entrypoint: Some(Entrypoint { entrypoint_type: EntrypointType::File, uri: None, diff --git a/src/commands/edge_app/utils.rs b/src/commands/edge_app/utils.rs index e0978d5..5b78aa4 100644 --- a/src/commands/edge_app/utils.rs +++ b/src/commands/edge_app/utils.rs @@ -333,6 +333,7 @@ mod tests { icon: Some("asdf".to_string()), author: Some("asdf".to_string()), homepage_url: Some("asdfasdf".to_string()), + categories: vec!["Utilities".to_string(), "Dashboards".to_string()], entrypoint: Some(Entrypoint { entrypoint_type: EntrypointType::File, uri: Some("entrypoint.html".to_string()), @@ -569,6 +570,7 @@ mod tests { icon: Some("asdf".to_string()), author: Some("asdf".to_string()), homepage_url: Some("asdfasdf".to_string()), + categories: vec!["Utilities".to_string(), "Dashboards".to_string()], entrypoint: Some(Entrypoint { entrypoint_type: EntrypointType::File, uri: Some("entrypoint.html".to_string()), @@ -1180,6 +1182,7 @@ mod tests { icon: Some("asdf".to_string()), author: Some("asdf".to_string()), homepage_url: Some("asdfasdf".to_string()), + categories: vec!["Utilities".to_string(), "Dashboards".to_string()], entrypoint: Some(Entrypoint { entrypoint_type: EntrypointType::File, uri: None, @@ -1221,6 +1224,7 @@ mod tests { icon: Some("asdf".to_string()), author: Some("asdf".to_string()), homepage_url: Some("asdfasdf".to_string()), + categories: vec!["Utilities".to_string(), "Dashboards".to_string()], entrypoint: Some(Entrypoint { entrypoint_type: EntrypointType::RemoteLocal, uri: None, @@ -1253,6 +1257,7 @@ mod tests { icon: Some("asdf".to_string()), author: Some("asdf".to_string()), homepage_url: Some("asdfasdf".to_string()), + categories: vec!["Utilities".to_string(), "Dashboards".to_string()], entrypoint: Some(Entrypoint { entrypoint_type: EntrypointType::RemoteLocal, uri: None,