Skip to content
Merged
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
74 changes: 73 additions & 1 deletion src/api/edge_app/setting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,39 @@ fn is_structured_help_text(help_text: &str) -> bool {
serde_json::from_str::<Value>(help_text).is_ok_and(|value| value.is_object())
}

fn properties_are_malformed(object: &serde_json::Map<String, Value>) -> bool {
matches!(object.get("properties"), Some(value) if !value.is_object())
}

pub(crate) fn extract_display_help_text(help_text: &Value) -> String {
match help_text {
Value::Object(object) if !properties_are_malformed(object) => object
.get("properties")
.and_then(|properties| properties.get("help_text"))
.and_then(|value| value.as_str())
.unwrap_or_default()
.to_string(),
Value::String(raw) if raw.trim_start().starts_with('{') => {
match serde_json::from_str::<Value>(raw) {
Ok(Value::Object(object)) if !properties_are_malformed(&object) => object
.get("properties")
.and_then(|properties| properties.get("help_text"))
.and_then(|value| value.as_str())
.unwrap_or_default()
.to_string(),
_ => raw.clone(),
}
}
Value::String(raw) => raw.clone(),
_ => String::new(),
}
}

fn has_malformed_properties(help_text: &str) -> bool {
let Ok(Value::Object(object)) = serde_json::from_str::<Value>(help_text) else {
return false;
};
matches!(object.get("properties"), Some(value) if !value.is_object())
properties_are_malformed(&object)
}

pub fn help_text_with_display_order(name: &str, help_text: &str, display_order: usize) -> String {
Expand Down Expand Up @@ -594,6 +622,50 @@ mod display_order_tests {
assert_eq!(value["properties"]["display_order"], json!(1));
}

#[test]
fn extract_display_help_text_returns_the_nested_help_text() {
let structured = json!({
"schema_version": 1,
"properties": { "help_text": "Say hello", "display_order": 0 }
})
.to_string();

assert_eq!(extract_display_help_text(&json!(structured)), "Say hello");
}

#[test]
fn extract_display_help_text_returns_empty_when_properties_help_text_is_missing() {
let structured = json!({
"schema_version": 1,
"properties": { "type": "number", "display_order": 0 }
})
.to_string();

assert_eq!(extract_display_help_text(&json!(structured)), "");
}

#[test]
fn extract_display_help_text_returns_raw_json_for_malformed_properties() {
let malformed = json!({ "schema_version": 1, "properties": "nope" }).to_string();

assert_eq!(extract_display_help_text(&json!(malformed)), malformed);
}

#[test]
fn extract_display_help_text_accepts_a_nested_object_value() {
let structured = json!({
"schema_version": 1,
"properties": { "help_text": "Say hello", "display_order": 0 }
});

assert_eq!(extract_display_help_text(&structured), "Say hello");
}

#[test]
fn extract_display_help_text_returns_plain_string_verbatim() {
assert_eq!(extract_display_help_text(&json!("Say hello")), "Say hello");
}

#[test]
fn overridden_names_are_left_untouched() {
assert_eq!(
Expand Down
43 changes: 43 additions & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@
authentication: &Authentication,
endpoint: &str,
) -> Result<serde_json::Value, CommandError> {
let url = format!("{}/{}", &authentication.config.url, endpoint);

Check warning on line 173 in src/commands/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

redundant reference in `format!` argument

warning: redundant reference in `format!` argument --> src/commands/mod.rs:173:32 | 173 | let url = format!("{}/{}", &authentication.config.url, endpoint); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `authentication.config.url` | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#useless_borrows_in_formatting
debug!("GET {url}");
let mut headers = HeaderMap::new();
headers.insert("Prefer", "return=representation".parse()?);
Expand All @@ -185,7 +185,7 @@
debug!("GET {url} -> {status}");

if status != StatusCode::OK {
println!("Response: {:?}", &response.text());

Check warning on line 188 in src/commands/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

redundant reference in `println!` argument

warning: redundant reference in `println!` argument --> src/commands/mod.rs:188:36 | 188 | println!("Response: {:?}", &response.text()); | ^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `response.text()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#useless_borrows_in_formatting
return Err(CommandError::WrongResponseStatus(status.as_u16()));
}
Ok(serde_json::from_str(&response.text()?)?)
Expand All @@ -196,7 +196,7 @@
endpoint: &str,
payload: &T,
) -> Result<serde_json::Value, CommandError> {
let url = format!("{}/{}", &authentication.config.url, endpoint);

Check warning on line 199 in src/commands/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

redundant reference in `format!` argument

warning: redundant reference in `format!` argument --> src/commands/mod.rs:199:32 | 199 | let url = format!("{}/{}", &authentication.config.url, endpoint); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `authentication.config.url` | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#useless_borrows_in_formatting
let mut headers = HeaderMap::new();
headers.insert("Prefer", "return=representation".parse()?);

Expand All @@ -212,7 +212,7 @@

// Ok, No_Content are acceptable because some of our RPC code returns that.
if ![StatusCode::CREATED, StatusCode::OK, StatusCode::NO_CONTENT].contains(&status) {
debug!("Response: {:?}", &response.text()?);

Check warning on line 215 in src/commands/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

redundant reference in `debug!` argument

warning: redundant reference in `debug!` argument --> src/commands/mod.rs:215:34 | 215 | debug!("Response: {:?}", &response.text()?); | ^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `response.text()?` | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#useless_borrows_in_formatting
return Err(CommandError::WrongResponseStatus(status.as_u16()));
}
if status == StatusCode::NO_CONTENT {
Expand All @@ -223,13 +223,13 @@
}

pub fn delete(authentication: &Authentication, endpoint: &str) -> anyhow::Result<(), CommandError> {
let url = format!("{}/{}", &authentication.config.url, endpoint);

Check warning on line 226 in src/commands/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

redundant reference in `format!` argument

warning: redundant reference in `format!` argument --> src/commands/mod.rs:226:32 | 226 | let url = format!("{}/{}", &authentication.config.url, endpoint); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `authentication.config.url` | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#useless_borrows_in_formatting
let response = authentication.build_client()?.delete(url).send()?;

let status = response.status();

if ![StatusCode::OK, StatusCode::NO_CONTENT].contains(&status) {
debug!("Response: {:?}", &response.text()?);

Check warning on line 232 in src/commands/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

redundant reference in `debug!` argument

warning: redundant reference in `debug!` argument --> src/commands/mod.rs:232:34 | 232 | debug!("Response: {:?}", &response.text()?); | ^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `response.text()?` | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#useless_borrows_in_formatting
return Err(CommandError::WrongResponseStatus(status.as_u16()));
}
Ok(())
Expand All @@ -240,7 +240,7 @@
endpoint: &str,
payload: &T,
) -> anyhow::Result<serde_json::Value, CommandError> {
let url = format!("{}/{}", &authentication.config.url, endpoint);

Check warning on line 243 in src/commands/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

redundant reference in `format!` argument

warning: redundant reference in `format!` argument --> src/commands/mod.rs:243:32 | 243 | let url = format!("{}/{}", &authentication.config.url, endpoint); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `authentication.config.url` | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#useless_borrows_in_formatting
let mut headers = HeaderMap::new();
headers.insert("Prefer", "return=representation".parse()?);

Expand All @@ -253,7 +253,7 @@

let status = response.status();
if status != StatusCode::OK {
debug!("Response: {:?}", &response.text()?);

Check warning on line 256 in src/commands/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

redundant reference in `debug!` argument

warning: redundant reference in `debug!` argument --> src/commands/mod.rs:256:34 | 256 | debug!("Response: {:?}", &response.text()?); | ^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `response.text()?` | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#useless_borrows_in_formatting
return Err(CommandError::WrongResponseStatus(status.as_u16()));
}

Expand Down Expand Up @@ -425,6 +425,11 @@
}
return Cell::new("");
}
if field_name.eq("help_text") {
let help_text =
crate::api::edge_app::setting::extract_display_help_text(field_value);
return Cell::new(&help_text);
}
debug!("field_name: {field_name}, field_value: {field_value:?}");
Cell::new(field_value.as_str().unwrap_or_default())
},
Expand Down Expand Up @@ -687,6 +692,44 @@
assert!(!EdgeAppSettings::supports_csv());
}

#[test]
fn test_edge_app_settings_formatter_extracts_structured_help_text() {
let data = r#"[
{
"name": "enable_analytics",
"title": "Enable Analytics",
"edge_app_setting_values": [],
"default_value": "true",
"optional": true,
"type": "string",
"help_text": "{\"properties\":{\"display_order\":0,\"help_text\":\"Enable or disable Sentry and Google Analytics integrations.\"},\"schema_version\":1}"
},
{
"name": "override_locale",
"title": "Override Locale",
"edge_app_setting_values": [],
"default_value": "en",
"optional": true,
"type": "string",
"help_text": "Override the default locale with a supported language code."
}
]"#;
let settings = EdgeAppSettings::new(serde_json::from_str(data).unwrap());

let output = settings.format(OutputType::HumanReadable);
assert_eq!(
output,
r#"+------------------+------------------+-------+---------------+----------+--------+-------------------------------------------------------------+
| Name | Title | Value | Default value | Optional | Type | Help text |
+------------------+------------------+-------+---------------+----------+--------+-------------------------------------------------------------+
| enable_analytics | Enable Analytics | | true | Yes | string | Enable or disable Sentry and Google Analytics integrations. |
+------------------+------------------+-------+---------------+----------+--------+-------------------------------------------------------------+
| override_locale | Override Locale | | en | Yes | string | Override the default locale with a supported language code. |
+------------------+------------------+-------+---------------+----------+--------+-------------------------------------------------------------+
"#
);
}

#[test]
fn test_edge_app_instance_formatter_format_output_properly() {
let data = r#"[{
Expand Down
Loading