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
44 changes: 44 additions & 0 deletions src/commands/notebooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,50 @@ pub async fn update(cfg: &Config, notebook_id: i64, file: &str) -> Result<()> {
formatter::output(cfg, &resp)
}

/// Append-only update: fetches the current notebook, appends cells from
/// `file` (an array of cell objects), then writes the full modified notebook back.
pub async fn edit(cfg: &Config, notebook_id: i64, file: &str) -> Result<()> {
let api = crate::make_api!(NotebooksAPI, cfg);

// Fetch current notebook so we can append without clobbering existing cells.
let current = api
.get_notebook(notebook_id)
.await
.map_err(|e| anyhow::anyhow!("failed to fetch notebook {notebook_id}: {e:?}"))?;

// Serialize current notebook to Value so we can manipulate cells generically.
let mut nb: serde_json::Value = serde_json::to_value(&current)
.map_err(|e| anyhow::anyhow!("failed to serialize notebook: {e:?}"))?;

// Read the new cells to append from the file (expected: array of cell objects).
let new_cells: serde_json::Value = util::read_json_file(file)?;
let new_cells_arr = new_cells
.as_array()
.ok_or_else(|| anyhow::anyhow!("--file must contain a JSON array of cell objects"))?;

// Append new cells to the existing cells array.
let cells = nb
.pointer_mut("/data/attributes/cells")
.and_then(|v| v.as_array_mut())
.ok_or_else(|| anyhow::anyhow!("could not locate cells array in notebook response"))?;
cells.extend(new_cells_arr.iter().cloned());

// Write back via the typed update endpoint.
let update_body: NotebookUpdateRequest = serde_json::from_value(
nb.get("data")
.cloned()
.map(|data| serde_json::json!({ "data": data }))
.unwrap_or(nb.clone()),
)
.map_err(|e| anyhow::anyhow!("failed to build update request: {e:?}"))?;

let resp = api
.update_notebook(notebook_id, update_body)
.await
.map_err(|e| anyhow::anyhow!("failed to edit notebook: {e:?}"))?;
formatter::output(cfg, &resp)
}

#[cfg(test)]
mod tests {

Expand Down
14 changes: 13 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5644,12 +5644,21 @@ enum NotebookActions {
#[arg(long, help = "JSON file with notebook data (required)")]
file: String,
},
/// Update a notebook
/// Update a notebook (full replace)
Update {
notebook_id: i64,
#[arg(long, help = "JSON file with notebook data (required)")]
file: String,
},
/// Append cells to an existing notebook (reads current notebook first, then appends)
Edit {
notebook_id: i64,
#[arg(
long,
help = "JSON file containing an array of cell objects to append (required)"
)]
file: String,
},
/// Delete a notebook
Delete { notebook_id: i64 },
}
Expand Down Expand Up @@ -12041,6 +12050,9 @@ async fn main_inner() -> anyhow::Result<()> {
NotebookActions::Update { notebook_id, file } => {
commands::notebooks::update(&cfg, notebook_id, &file).await?;
}
NotebookActions::Edit { notebook_id, file } => {
commands::notebooks::edit(&cfg, notebook_id, &file).await?;
}
NotebookActions::Delete { notebook_id } => {
commands::notebooks::delete(&cfg, notebook_id).await?;
}
Expand Down
Loading