zot provides a preview, approval-record, execution,
ledger, and verification sequence for batch plans. This vignette runs
the sequence against an in-memory request performer. It makes no network
requests and changes no Zotero library.
A plan is a data.table with one action per row.
Supported actions are "patch", "create", and
"trash". Patch and Trash rows identify an existing item;
create rows contain a new-item payload.
library(data.table)
library(zot)
Plan <- data.table(
action = "patch",
library = "user",
itemKey = "ABCD1234",
payload = list(list(title = "A reviewed title"))
)
Preview <- zotPreview(Plan, describe = "Correct one reviewed title")
Preview
#> <zotPreview> Correct one reviewed title
#> digest: 34f12497d5349ed6e1397c32f5bedfa29625bb9006ad73c5258cb4aa38f0a9b3
#> action library N
#> <char> <char> <int>
#> 1: patch user 1zotPreview() validates the required columns and action
values, copies the rows, and records a SHA-256 digest. It does not make
an API request.
zotApprove() stores the preview digest, optional notes,
and a timestamp.
Approval <- zotApprove(Preview, notes = "Offline documentation example")
identical(Approval$digest, Preview$digest)
#> [1] TRUEThe function records application metadata; it does not authenticate a
person or determine whether execution is authorized. The executor
requires the approval and preview digests to match. Callers should not
modify preview$plan after creating the approval because the
current executor does not recalculate its digest.
The performer below models a versioned Zotero item. It supports the GET and PATCH calls used by the example.
Store <- new.env(parent = emptyenv())
Store$version <- 1L
Store$title <- "Original title"
Performer <- function(config, method, path, query, body, version) {
if (method == "GET") {
return(list(
status = 200L,
headers = list(),
body = list(
version = Store$version,
data = list(title = Store$title)
)
))
}
if (method == "PATCH" && identical(as.integer(version), Store$version)) {
Store$title <- body$title
Store$version <- Store$version + 1L
return(list(status = 204L, headers = list(), body = NULL))
}
list(status = 412L, headers = list(), body = NULL)
}
Config <- zotConfig(
userID = "42",
key = "example-key",
performer = Performer
)
LedgerPath <- tempfile(fileext = ".json")
Ledger <- zotExecute(
Config,
preview = Preview,
approval = Approval,
ledgerPath = LedgerPath
)
Ledger$items$ABCD1234$status
#> [1] "done"
Store$title
#> [1] "A reviewed title"For a patch row, zotExecute() reads the current item
version, sends a conditional patch, reads the item again, and compares
the requested fields. A matching result is recorded as
"done". Create rows are recorded as done after the new item
can be read; Trash rows use the status returned by
zotTrash().
Reusing the ledger path for the same digest skips entries already marked done:
zotVerify() summarizes ledger statuses and re-reads at
most ten completed patch rows. It does not sample create or Trash
rows.
Verification <- zotVerify(Config, preview = Preview, ledger = LedgerAgain)
Verification
#> $counts
#> Statuses
#> done
#> 1
#>
#> $sampleChecked
#> [1] 1
#>
#> $sampleConfirmed
#> [1] 1
unlink(LedgerPath)Applications should inspect failed or missing ledger entries separately. Local SQLite and semantic-index results are useful for discovery but do not replace an API read of remote state.