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
5 changes: 5 additions & 0 deletions .changeset/fix-append-json-multirow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@googleworkspace/cli": patch
---

Fix `+append --json-values` multi-row bug by changing AppendConfig.values to Vec<Vec<String>>
43 changes: 33 additions & 10 deletions src/helpers/sheets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ fn build_append_request(
// This allows us to easily create nested objects without defining explicit structs
// for every API request body.
let body = json!({
"values": [config.values]
"values": config.values
});

// Map `&String` scope URLs to owned `String`s for the return value
Expand Down Expand Up @@ -263,8 +263,8 @@ fn build_read_request(
pub struct AppendConfig {
/// The ID of the spreadsheet to append to.
pub spreadsheet_id: String,
/// The values to append, as a vector of strings.
pub values: Vec<String>,
/// The values to append, as a 2D array of strings (one inner vec per row).
pub values: Vec<Vec<String>>,
}

/// Parses arguments for the `+append` command.
Expand All @@ -274,13 +274,15 @@ pub fn parse_append_args(matches: &ArgMatches) -> AppendConfig {
let values = if let Some(json_str) = matches.get_one::<String>("json-values") {
// Parse JSON array of rows
if let Ok(parsed) = serde_json::from_str::<Vec<Vec<String>>>(json_str) {
parsed.into_iter().flatten().collect()
parsed
} else {
// Treat as single row JSON array
serde_json::from_str::<Vec<String>>(json_str).unwrap_or_default()
serde_json::from_str::<Vec<String>>(json_str)
.map(|row| vec![row])
.unwrap_or_default()
Comment on lines +280 to +282
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using unwrap_or_default() here silently ignores JSON parsing errors. If a user provides malformed JSON to --json-values, the command will proceed with an empty list of values instead of reporting an error. This can be misleading and cause users to believe their data was appended when it was not. The command should fail with a clear error message on invalid input.

A proper fix would involve changing parse_append_args to return a Result<AppendConfig, GwsError> to propagate the error from serde_json. Since that requires changes outside the modified lines, the suggestion below will panic on error to make the failure explicit, which is preferable to silent failure.

Suggested change
serde_json::from_str::<Vec<String>>(json_str)
.map(|row| vec![row])
.unwrap_or_default()
serde_json::from_str::<Vec<String>>(json_str)
.map(|row| vec![row])
.unwrap_or_else(|e| panic!("Invalid --json-values: {}. Failed to parse as a 2D or 1D array of strings.", e))

}
} else if let Some(values_str) = matches.get_one::<String>("values") {
values_str.split(',').map(|s| s.to_string()).collect()
vec![values_str.split(',').map(|s| s.to_string()).collect()]
} else {
Vec::new()
};
Expand Down Expand Up @@ -365,14 +367,18 @@ mod tests {
let doc = make_mock_doc();
let config = AppendConfig {
spreadsheet_id: "123".to_string(),
values: vec!["a".to_string(), "b".to_string(), "c".to_string()],
values: vec![vec!["a".to_string(), "b".to_string(), "c".to_string()]],
};
let (params, body, scopes) = build_append_request(&config, &doc).unwrap();

assert!(params.contains("123"));
assert!(params.contains("USER_ENTERED"));
assert!(body.contains("a"));
assert!(body.contains("b"));

let body_json: serde_json::Value = serde_json::from_str(&body).unwrap();
assert_eq!(
body_json,
serde_json::json!({"values": [["a", "b", "c"]]})
);
assert_eq!(scopes[0], "https://scope");
}

Expand All @@ -395,7 +401,24 @@ mod tests {
let matches = make_matches_append(&["test", "--spreadsheet", "123", "--values", "a,b,c"]);
let config = parse_append_args(&matches);
assert_eq!(config.spreadsheet_id, "123");
assert_eq!(config.values, vec!["a", "b", "c"]);
assert_eq!(config.values, vec![vec!["a", "b", "c"]]);
}

#[test]
fn test_parse_append_args_json_multirow() {
let matches = make_matches_append(&[
"test",
"--spreadsheet",
"123",
"--json-values",
r#"[["Alice","100"],["Bob","200"]]"#,
]);
let config = parse_append_args(&matches);
assert_eq!(config.spreadsheet_id, "123");
assert_eq!(
config.values,
vec![vec!["Alice", "100"], vec!["Bob", "200"]]
);
}

#[test]
Expand Down