Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/CommandLineHelp.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This document contains the help content for the `screenly` command-line program.
* [`screenly screen list`↴](#screenly-screen-list)
* [`screenly screen get`↴](#screenly-screen-get)
* [`screenly screen add`↴](#screenly-screen-add)
* [`screenly screen status`↴](#screenly-screen-status)
* [`screenly screen delete`↴](#screenly-screen-delete)
* [`screenly asset`↴](#screenly-asset)
* [`screenly asset list`↴](#screenly-asset-list)
Expand Down Expand Up @@ -97,6 +98,7 @@ Screen related commands
* `list` — Lists your screens
* `get` — Gets a single screen by id
* `add` — Adds a new screen
* `status` — Shows screen counts: total, online/offline, out of sync
* `delete` — Deletes a screen. This cannot be undone


Expand Down Expand Up @@ -146,6 +148,18 @@ Adds a new screen



## `screenly screen status`

Shows screen counts: total, online/offline, out of sync

**Usage:** `screenly screen status [OPTIONS]`

###### **Options:**

* `-j`, `--json` — Enables JSON output



## `screenly screen delete`

Deletes a screen. This cannot be undone
Expand Down
21 changes: 21 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ pub enum ScreenCommands {
/// Optional name of the new screen.
name: Option<String>,
},
/// Shows screen counts: total, online/offline, out of sync.
Status {
/// Enables JSON output.
#[arg(short, long, action = clap::ArgAction::SetTrue)]
json: Option<bool>,
},
/// Deletes a screen. This cannot be undone.
Delete {
/// UUID of the screen to be deleted.
Expand Down Expand Up @@ -649,6 +655,21 @@ pub fn handle_cli_screen_command(command: &ScreenCommands) {
ScreenCommands::Add { pin, name, json } => {
handle_command_execution_result(screen_command.add(pin, name.clone()), json);
}
ScreenCommands::Status { json } => {
let screen_status_command = commands::screen::ScreenCommand::new(get_authentication());
let output_type = if json == &Some(true) {
OutputType::Json
} else {
OutputType::HumanReadable
};
match screen_status_command.status() {
Ok(status) => println!("{}", status.format(output_type)),
Err(e) => {
error!("Error occurred: {e:?}");
std::process::exit(1);
}
}
}
ScreenCommands::Delete { uuid } => {
match get_screen_name(uuid, &screen_command) {
Ok(name) => {
Expand Down
123 changes: 122 additions & 1 deletion src/commands/screen.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::collections::HashMap;

use prettytable::{row, Table};
use reqwest::StatusCode;
use serde::Serialize;

use crate::authentication::Authentication;
use crate::commands;
use crate::commands::{CommandError, Screens};
use crate::commands::{CommandError, OutputType, Screens};

pub struct ScreenCommand {
authentication: Authentication,
Expand Down Expand Up @@ -64,6 +66,55 @@ impl ScreenCommand {
let endpoint = format!("v3/screens/{id}/");
commands::delete(&self.authentication, &endpoint)
}

pub fn status(&self) -> Result<ScreensStatus, CommandError> {
let data = commands::get(&self.authentication, "v4/screens?select=id,status,in_sync")?;
let screens = data.as_array().map(|a| a.as_slice()).unwrap_or(&[]);

let total = screens.len();
let online = screens
.iter()
.filter(|s| s["status"].as_str() == Some("Online"))
.count();
let out_of_sync = screens
.iter()
.filter(|s| s["in_sync"].as_bool() != Some(true))
.count();

Ok(ScreensStatus {
total,
online,
offline: total - online,
out_of_sync,
})
}
}

#[derive(Debug, Serialize)]
pub struct ScreensStatus {
pub total: usize,
pub online: usize,
pub offline: usize,
pub out_of_sync: usize,
}

impl ScreensStatus {
pub fn format(&self, output_type: OutputType) -> String {
match output_type {
OutputType::Json => serde_json::to_string_pretty(self).unwrap(),
OutputType::HumanReadable => {
let mut table = Table::new();
table.add_row(row!["Total", "Online", "Offline", "Out of Sync"]);
table.add_row(row![
self.total,
self.online,
self.offline,
self.out_of_sync
]);
table.to_string()
}
}
}
}

#[cfg(test)]
Expand Down Expand Up @@ -188,4 +239,74 @@ mod tests {

assert_eq!(screen.format(OutputType::HumanReadable), expected_output);
}

fn make_screen(status: &str, in_sync: bool) -> serde_json::Value {
json!({ "id": "abc", "status": status, "in_sync": in_sync })
}

#[test]
fn test_screens_counts_online_offline_and_out_of_sync() {
let screens = json!([
make_screen("Online", true),
make_screen("Online", false),
make_screen("Offline", true),
make_screen("Offline", false),
]);

let mock_server = MockServer::start();
mock_server.mock(|when, then| {
when.method(GET)
.path("/v4/screens")
.query_param("select", "id,status,in_sync");
then.status(200).json_body(screens);
});

let auth = Authentication::new_with_config(Config::new(mock_server.base_url()), "token");
let result = ScreenCommand::new(auth).status().unwrap();

assert_eq!(result.total, 4);
assert_eq!(result.online, 2);
assert_eq!(result.offline, 2);
assert_eq!(result.out_of_sync, 2);
}

#[test]
fn test_screens_all_online_and_in_sync() {
let screens = json!([make_screen("Online", true), make_screen("Online", true),]);

let mock_server = MockServer::start();
mock_server.mock(|when, then| {
when.method(GET)
.path("/v4/screens")
.query_param("select", "id,status,in_sync");
then.status(200).json_body(screens);
});

let auth = Authentication::new_with_config(Config::new(mock_server.base_url()), "token");
let result = ScreenCommand::new(auth).status().unwrap();

assert_eq!(result.total, 2);
assert_eq!(result.online, 2);
assert_eq!(result.offline, 0);
assert_eq!(result.out_of_sync, 0);
}

#[test]
fn test_screens_empty() {
let mock_server = MockServer::start();
mock_server.mock(|when, then| {
when.method(GET)
.path("/v4/screens")
.query_param("select", "id,status,in_sync");
then.status(200).json_body(json!([]));
});

let auth = Authentication::new_with_config(Config::new(mock_server.base_url()), "token");
let result = ScreenCommand::new(auth).status().unwrap();

assert_eq!(result.total, 0);
assert_eq!(result.online, 0);
assert_eq!(result.offline, 0);
assert_eq!(result.out_of_sync, 0);
}
}
Loading