Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
6a69329
first crack at validation, fake data passing
jeanetteclark Mar 20, 2026
fbda176
improve validation based on running through CDFW unfiltered and then …
jeanetteclark Mar 20, 2026
7fbe0f9
Update SWH ID
rushirajnenuji Mar 21, 2026
e4e7dba
Merge pull request #87 from NCEAS/feat-86-swh-id
mbjones Mar 21, 2026
f9690f4
Support detail=minimal for taxon observations
regetz Mar 21, 2026
665035f
Support detail=minimal for taxon importances
regetz Mar 21, 2026
ea44df9
Support detail=minimal for stem counts
regetz Mar 21, 2026
e29ab8f
Support detail=minimal for strata
regetz Mar 21, 2026
9ca8d3a
Update vb_get docs with `detail` arg changes
regetz Mar 21, 2026
9003267
Merge pull request #89 from NCEAS/feature-88-minimal-detail
regetz Mar 21, 2026
c607a69
Add docs and tests for search by vb_code
regetz Mar 21, 2026
acc4fea
Merge pull request #91 from NCEAS/feature-90-search-by-code
regetz Mar 21, 2026
5a75e0c
Add vb_upload_taxon_interpretations()
regetz Mar 21, 2026
e0f10a4
Add vb_upload_community_classifications()
regetz Mar 21, 2026
3fbcac2
Merge branch 'main' into feature-79-validation
jeanetteclark Mar 23, 2026
af124e7
Merge branch 'develop' into feature-79-validation
jeanetteclark Mar 23, 2026
003f77e
set required status for a few rows where it was unset
jeanetteclark Mar 23, 2026
dddfb6f
Add vb_upload_cover_methods()
regetz Mar 23, 2026
a5fb210
Add vb_upload_stratum_methods()
regetz Mar 23, 2026
b551af0
Merge pull request #95 from NCEAS/feature-93-add-more-uploaders
regetz Mar 23, 2026
ccd7e54
Add vb_create_dataset()
regetz Mar 24, 2026
d9e4d8f
edits to LT sections and add an upload section
jeanetteclark Mar 24, 2026
dbc01b1
merge in develop
jeanetteclark Mar 24, 2026
b2e5bc0
rename test file
jeanetteclark Mar 24, 2026
a6cad46
make output a little quieter regarding skipped tests
jeanetteclark Mar 24, 2026
9d9b972
add stem data validation
jeanetteclark Mar 24, 2026
278c454
add more tests and refine output
jeanetteclark Mar 24, 2026
b3e0d5b
Add info to upload.Rmd on getting contributor status before upload.
mbjones Mar 24, 2026
4e5a9f6
Created data download vignette
maggieklope Mar 24, 2026
24b986d
Merge branch 'feature-75-documentation-2' of https://github.com/NCEAS…
maggieklope Mar 25, 2026
bacd8b6
Merge pull request #96 from NCEAS/feature-94-create-user-dataset
regetz Mar 25, 2026
a06afe5
add validation for plant concepts and community concepts
jeanetteclark Mar 25, 2026
cd9147f
add test for community concepts
jeanetteclark Mar 25, 2026
89b6aa4
add validation functions for cover, strata methods and improve tests
jeanetteclark Mar 25, 2026
ed3367b
add optional argument for fk validation
jeanetteclark Mar 25, 2026
e7e5211
merge develop and resolve conflict
jeanetteclark Mar 25, 2026
8caa63b
Refining text and examples
maggieklope Mar 25, 2026
949abc2
make optional fk validators quieter
jeanetteclark Mar 25, 2026
a05d1de
Merge branch 'develop' into feature-75-documentation-2
jeanetteclark Mar 25, 2026
394902e
include download page in vegbankr website
jeanetteclark Mar 25, 2026
e5d8888
make info message a warning if check is not optional
jeanetteclark Mar 25, 2026
849cd57
turn warnings to dangers
jeanetteclark Mar 25, 2026
7373d8a
add validation description
jeanetteclark Mar 25, 2026
7f9e8f9
don't execute chunk, and fix yaml
jeanetteclark Mar 25, 2026
c3755d8
minor copy edits
jeanetteclark Mar 25, 2026
e9d1d81
add download to navbar
jeanetteclark Mar 25, 2026
35187bb
missed one header edit
jeanetteclark Mar 25, 2026
d2f9c0f
Added examples using dplyr, added better documentation
maggieklope Mar 26, 2026
df4d892
fixing VegBank capitalization
maggieklope Mar 26, 2026
6c0fc9b
small copy edits
maggieklope Mar 26, 2026
cde998b
Support status filter for relevant getters
regetz Mar 26, 2026
2940728
Adjust API tests to match 2.1.0 response schemas
regetz Mar 26, 2026
dbbe117
make upload response a little easier to understand
jeanetteclark Mar 26, 2026
d78e2c7
Add API upload tests for local testing (not run automatically)
regetz Mar 26, 2026
4cfb956
Fix README.md link
regetz Mar 26, 2026
09f17ff
Merge pull request #103 from NCEAS/feature-99-status-filter
regetz Mar 26, 2026
5b67e06
Stop with error if token refresh is needed but fails.
mbjones Mar 26, 2026
adc09b1
add disturbance allowed values table
jeanetteclark Mar 26, 2026
8537ffd
Merge pull request #101 from NCEAS/feature-100-upload-output
jeanetteclark Mar 26, 2026
3d4caa3
add Maggie as contributor
jeanetteclark Mar 26, 2026
077ce22
fix typo and replace tibble with data frame
jeanetteclark Mar 26, 2026
7bf95f4
Update refresh condition with a valid buffer
rushirajnenuji Mar 26, 2026
51258ba
Merge branch 'bugfix-jwt-expiry-check' into bug-81-refresh-failure
rushirajnenuji Mar 26, 2026
97c9d51
Undo comment change resulting from merge
rushirajnenuji Mar 26, 2026
ed4e27c
Remove extraneous Untitled.R test file that was accidentally committed.
mbjones Mar 26, 2026
9c7ab77
Fix code comment
mbjones Mar 26, 2026
5b03cc6
Merge pull request #104 from NCEAS/bug-81-refresh-failure
mbjones Mar 26, 2026
2bf5279
drop validate_at_least_one_present for optional xor fields
jeanetteclark Mar 26, 2026
05969c3
merge in develop, resolve namespace conflict
jeanetteclark Mar 26, 2026
1bc6ddf
Merge pull request #105 from NCEAS/feature-75-documentation-2
jeanetteclark Mar 26, 2026
fb03264
Merge pull request #97 from NCEAS/feature-79-validation
jeanetteclark Mar 27, 2026
7fed32e
Bump version to 1.0.0 in prep for release.
mbjones Mar 27, 2026
1063e0d
Fix spelling errors/acronyms throughout package.
mbjones Mar 27, 2026
d473b49
Update DOI for 1.0.0 release.
mbjones Mar 27, 2026
733df2c
Ignore revdepcheck folders
mbjones Mar 27, 2026
0e9af3e
Add NEWS sections
mbjones Mar 27, 2026
67b4552
removing datacite xml file to avoid chick and egg release conundrum
mbjones Mar 27, 2026
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
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
^docs$
^pkgdown$
^.*\.Rproj$
^revdep$
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ po/*~
# RStudio Connect folder
rsconnect/
docs

# revdepcheck folder
revdep/
14 changes: 10 additions & 4 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: vegbankr
Title: Interface to the 'VegBank' Vegetation Plot Database
Version: 0.9.0
Date: 2026-03-20
Version: 1.0.0
Date: 2026-03-26
Authors@R: c(
person("Jim", "Regetz",
email = "regetz@nceas.ucsb.edu",
Expand All @@ -18,7 +18,11 @@ Authors@R: c(
person("Jeanette", "Clark",
email = "jclark@nceas.ucsb.edu",
role = c("aut"),
comment=c(ORCID = "0000-0003-4703-1974"))
comment=c(ORCID = "0000-0003-4703-1974")),
person("Maggie", "Klope",
email = "mmklope@nceas.ucsb.edu",
role = c("aut"),
comment=c(ORCID = "0000-0003-3926-7039"))
)
Description: Provides a programmatic interface to 'VegBank', the
vegetation plot database of the Ecological Society of America's
Expand All @@ -41,14 +45,16 @@ RoxygenNote: 7.3.3
Imports:
DBI,
base64enc,
cli,
curl,
dplyr,
duckdb,
httr2,
jsonlite,
nanoparquet,
purrr,
rlang
rlang,
tidyr
Suggests:
DT,
knitr,
Expand Down
14 changes: 14 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export(vb_count_stratum_methods)
export(vb_count_taxon_importances)
export(vb_count_taxon_observations)
export(vb_count_user_datasets)
export(vb_create_dataset)
export(vb_debug)
export(vb_get)
export(vb_get_base_url)
Expand Down Expand Up @@ -51,12 +52,25 @@ export(vb_set_token)
export(vb_undebug)
export(vb_unset_token)
export(vb_upload)
export(vb_upload_community_classifications)
export(vb_upload_community_concepts)
export(vb_upload_cover_methods)
export(vb_upload_plant_concepts)
export(vb_upload_plot_observations)
export(vb_upload_stratum_methods)
export(vb_upload_taxon_interpretations)
export(vb_validate_community_concepts)
export(vb_validate_cover_methods)
export(vb_validate_plant_concepts)
export(vb_validate_plot_observations)
export(vb_validate_stratum_methods)
import(cli)
import(curl)
import(dplyr)
import(httr2)
import(nanoparquet)
import(tidyr)
importFrom(jsonlite,toJSON)
importFrom(rlang,"!!!")
importFrom(rlang,"!!")
importFrom(rlang,":=")
Expand Down
23 changes: 22 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
# vegbankr 1.0.0

Production release of the VegBank API client, version 1.0.0 on 2026-03-26.

New features:

- write validation function [#78](https://github.com/NCEAS/vegbankr/issues/78)
- Support new "minimal" detail when getting various entities [#88](https://github.com/NCEAS/vegbankr/issues/88)
- Document and test updated search behavior [#90](https://github.com/NCEAS/vegbankr/issues/90)
- Fully support the VegBank 2.0.0 write API [#92](https://github.com/NCEAS/vegbankr/issues/92)
- Add functions for remaining VegBank upload endpoints [#93](https://github.com/NCEAS/vegbankr/issues/93)
- Support creation of user datasets [#94](https://github.com/NCEAS/vegbankr/issues/94)
- Add user-friendly upload data validation [#79](https://github.com/NCEAS/vegbankr/issues/79)
- Support status parameter in concept/observation getters [#99](https://github.com/NCEAS/vegbankr/issues/99)
- make upload output more easily understandable without lots of scrolling [#100](https://github.com/NCEAS/vegbankr/issues/100)
- Add upload sanity tests that can be run locally [#102](https://github.com/NCEAS/vegbankr/issues/102)

Bug fixes:

- logical bug when vb_refresh_tokens() fails to get a token [#81](https://github.com/NCEAS/vegbankr/issues/81)

# vegbankr 0.9.0

First production release of the VegBank API client;
First production release of the VegBank API client, version 0.9.0 on 2026-03-20.

- Initial API development release
- Complete coverage of read API endpoints
Expand Down
13 changes: 8 additions & 5 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ vb_set_base_url <- function(vb_base_url, port) {
#' Gets the base URL for the VegBank API. If previously set by the user,
#' e.g. via `vb_set_base_url()`, this will be pulled from the global
#' option (`vegbank.base_api_url`). If this option is unset, the package
#' default value of "https://api.vegbank.org" will be used.
#' default value of `https://api.vegbank.org` will be used.
#'
#' @returns A length-one character vector containing the base URL string
#' @seealso [vb_set_base_url()]
Expand Down Expand Up @@ -132,15 +132,18 @@ send <- function(request, skip_auth = FALSE) {

if (!skip_auth) {
# If the access token is expired but the refresh token is
# still valid, update tokens before retrying the request.
if (!is.null(vb_get_access_token()) && (!vb_access_token_is_valid() || (Sys.time() + 30) < jwt_expiry_time(vb_get_access_token()))) {
# still valid, update tokens before trying the request.
if (!is.null(vb_get_access_token()) && (!vb_access_token_is_valid() || jwt_expiry_time(vb_get_access_token()) < (Sys.time() + 30))) {
if (vb_refresh_token_is_valid()) {
message("Access token expired; refreshing tokens...")
vb_refresh_tokens()
if (!is.null(token) && !vb_access_token_is_valid()) {
message("Access token expired and token refresh failed. Please re-authenticate at https://api.vegbank.org/login to get new tokens.")
stop("Re-authenticate and call vb_set_token() to set a new token.", call. = FALSE)
}
} else {
message("Access token expired and no valid refresh token available. Please re-authenticate at https://api.vegbank.org/login to get new tokens.")
stop("Re-authenticate and call vb_set_token() to set a new token.",
call. = FALSE)
stop("Re-authenticate and call vb_set_token() to set a new token.", call. = FALSE)
}
}

Expand Down
97 changes: 97 additions & 0 deletions R/vb_create_dataset.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#' Create a VegBank dataset
#'
#' @description
#' Use the VegBank API to create a user dataset, which defines a
#' collection of plot observations that can be cited in VegBank.
#'
#' @param name A single character string giving the dataset name. Must be 100
#' characters or fewer.
#' @param description A single character string describing the dataset.
#' @param observations A character vector of observation codes. Each element
#' must match the pattern \code{"ob.<positive_integer>"} (e.g.
#' \code{"ob.2948"}), and must refer to a plot observation in VegBank. The
#' vector must not be empty.
#' @param dry_run Logical indicating whether to perform a dry run. If `TRUE`,
#' the API will validate the data without committing changes to the database.
#' Default is `FALSE`.
#'
#' @return The processed response object from the VegBank API documenting what
#' (if anything) was successfully created in VegBank.
#'
#' @examples
#' \dontrun{
#' vb_create_dataset(
#' name = "Test Dataset 001",
#' description = "A test dataset containing 10 observations",
#' observations = paste0("ob.", 2948:2957)
#' )
#' }
#'
#' @export
vb_create_dataset <- function(name, description, observations,
dry_run = FALSE) {

# Validate inputs
if (!is.character(name) || length(name) != 1L || is.na(name)) {
stop("`name` must be a single non-NA character string.")
}
if (nchar(name) > 100L) {
stop("`name` must be 100 characters or fewer.")
}

if (!is.character(description) || length(description) != 1L ||
is.na(description)) {
stop("`description` must be a single non-NA character string.")
}

if (!is.character(observations) || length(observations) == 0L) {
stop("`observations` must be a non-empty character vector.")
}

ob_pattern <- "^ob\\.[1-9][0-9]*$"
invalid <- observations[!grepl(ob_pattern, observations)]
if (length(invalid) > 0L) {
stop(
"`observations` contains invalid codes: ",
paste(invalid, collapse = ", ")
)
}

request <- build_dataset_request(name, description, observations, dry_run)
response <- send(request)
handle_vb_upload_response(response)
}

#' Build httr2 request for POST /user-datasets
#'
#' @param name A single character string giving the dataset name.
#' @param description A single character string describing the dataset.
#' @param observations A character vector of VegBank observation codes.
#' @param dry_run Logical indicating whether to perform a dry run. If `TRUE`,
#' the API will validate the data without committing changes to the database.
#' Default is `FALSE`.
#'
#' @return An httr2 response
#'
#' @import httr2
#' @importFrom jsonlite toJSON
#' @noRd
build_dataset_request <- function(name, description, observations,
dry_run = FALSE) {
# Assemble payload
json_payload <- list(
name = name,
description = description,
data = list(observation = as.list(observations))
) |> jsonlite::toJSON(pretty = TRUE, auto_unbox = TRUE)

# Build request
request <- request(vb_get_base_url()) |>
req_url_path_append("user-datasets") |>
req_method("POST") |>
req_url_query(dry_run = dry_run) |>
req_headers("Content-Type" = "application/json") |>
req_body_raw(json_payload, type = "application/json")

return(request)
}
49 changes: 32 additions & 17 deletions R/vb_get.R
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
#' between the two options. If not specified, defaults to `TRUE` for
#' collection queries and `FALSE` for single-record queries.
#' @param search Optional search string for filtering results based on full-text
#' search. Available for: plot-observations, plant-concepts, community-concepts,
#' projects, and parties.
#' search or the resource's vb_code (the latter of which is functionally
#' equivalent to using the `vb_code` argument directly). Available for:
#' plot-observations, plant-concepts, community-concepts, projects, and
#' parties.
#' @param sort Optional string for sorting results. Prepend with "-" for
#' descending order (e.g., "-obs_count"). Available for:
#' \itemize{
Expand All @@ -32,6 +34,15 @@
#' \item \strong{parties:} "default", "surname", "organization_name",
#' "obs_count"
#' }
#' @param status Optional string for limiting results by status, with
#' default equivalent to 'any' if unset. Available for:
#' \itemize{
#' \item \strong{plot-observations:} "any", "current"
#" \item \strong{plant-concepts:} "any", "current", "accepted",
#" "current_accepted"
#' \item \strong{community-concepts:} "any", "current", "accepted",
#' "current_accepted"
#' }
#' @param detail Character string specifying level of detail. All endpoints
#' support "full" detail. For those that support "minimal" detail, this is the
#' default for collection queries, otherwise the default is "full". Plot
Expand Down Expand Up @@ -257,68 +268,72 @@ vb_get_stratum_methods <- function(vb_code = NULL, limit = 100, offset = 0,
#' @rdname vb_get
#' @export
vb_get_plant_concepts <- function(vb_code = NULL, limit = 100, offset = 0,
parquet = NULL, search = NULL,
parquet = NULL, search = NULL, status = NULL,
with_nested = NULL, ...) {
resource <- "plant-concepts"
vb_key <- get_vb_key(resource)
is_collection <- is.null(vb_code) || substr(vb_code, 1, 2) != vb_key
if (missing(parquet)) parquet <- if (is_collection) TRUE else FALSE
if (missing(with_nested)) with_nested <- if (is_collection) FALSE else TRUE
vb_get(resource, vb_code, parquet = parquet, search = search,
vb_get(resource, vb_code, parquet = parquet, search = search, status = status,
with_nested = with_nested, limit = limit, offset = offset, ...)
}

#' @rdname vb_get
#' @export
vb_get_taxon_observations <- function(vb_code = NULL, limit = 100,
offset = 0, parquet = NULL,
search = NULL,
search = NULL, detail = NULL,
with_nested = NULL, ...) {
resource <- "taxon-observations"
vb_key <- get_vb_key(resource)
is_collection <- is.null(vb_code) || substr(vb_code, 1, 2) != vb_key
if (missing(parquet)) parquet <- if (is_collection) TRUE else FALSE
if (missing(detail)) detail <- if (is_collection) "minimal" else "full"
if (missing(with_nested)) with_nested <- if (is_collection) FALSE else TRUE
vb_get(resource, vb_code, parquet = parquet, search = search,
vb_get(resource, vb_code, parquet = parquet, search = search, detail = detail,
with_nested = with_nested, limit = limit, offset = offset, ...)
}

#' @rdname vb_get
#' @export
vb_get_taxon_importances <- function(vb_code = NULL, limit = 100,
offset = 0, parquet = NULL,
offset = 0, parquet = NULL, detail = NULL,
with_nested = NULL, ...) {
resource <- "taxon-importances"
vb_key <- get_vb_key(resource)
is_collection <- is.null(vb_code) || substr(vb_code, 1, 2) != vb_key
if (missing(parquet)) parquet <- if (is_collection) TRUE else FALSE
if (missing(detail)) detail <- if (is_collection) "minimal" else "full"
if (missing(with_nested)) with_nested <- if (is_collection) FALSE else TRUE
vb_get(resource, vb_code, parquet = parquet, with_nested = with_nested,
limit = limit, offset = offset, ...)
vb_get(resource, vb_code, parquet = parquet, detail = detail,
with_nested = with_nested, limit = limit, offset = offset, ...)
}

#' @rdname vb_get
#' @export
vb_get_stem_counts <- function(vb_code = NULL, limit = 100, offset = 0,
parquet = NULL, ...) {
parquet = NULL, detail = NULL, ...) {
resource <- "stem-counts"
vb_key <- get_vb_key(resource)
is_collection <- is.null(vb_code) || substr(vb_code, 1, 2) != vb_key
if (missing(detail)) detail <- if (is_collection) "minimal" else "full"
if (missing(parquet)) parquet <- if (is_collection) TRUE else FALSE
vb_get(resource, vb_code, parquet = parquet, limit = limit,
offset = offset, ...)
offset = offset, detail = detail, ...)
}

#' @rdname vb_get
#' @export
vb_get_strata <- function(vb_code = NULL, limit = 100, offset = 0,
parquet = NULL, ...) {
parquet = NULL, detail = NULL,...) {
resource <- "strata"
vb_key <- get_vb_key(resource)
is_collection <- is.null(vb_code) || substr(vb_code, 1, 2) != vb_key
if (missing(detail)) detail <- if (is_collection) "minimal" else "full"
if (missing(parquet)) parquet <- if (is_collection) TRUE else FALSE
vb_get(resource, vb_code, parquet = parquet, limit = limit,
offset = offset, ...)
offset = offset, detail = detail, ...)
}

#' @rdname vb_get
Expand All @@ -338,14 +353,14 @@ vb_get_taxon_interpretations <- function(vb_code = NULL, limit = 100,
#' @rdname vb_get
#' @export
vb_get_community_concepts <- function(vb_code = NULL, limit = 100, offset = 0,
parquet = NULL, search = NULL,
parquet = NULL, search = NULL, status = NULL,
with_nested = NULL, ...) {
resource <- "community-concepts"
vb_key <- get_vb_key(resource)
is_collection <- is.null(vb_code) || substr(vb_code, 1, 2) != vb_key
if (missing(parquet)) parquet <- if (is_collection) TRUE else FALSE
if (missing(with_nested)) with_nested <- if (is_collection) FALSE else TRUE
vb_get(resource, vb_code, parquet = parquet, search = search,
vb_get(resource, vb_code, parquet = parquet, search = search, status = status,
with_nested = with_nested, limit = limit, offset = offset, ...)
}

Expand Down Expand Up @@ -384,7 +399,7 @@ vb_get_community_interpretations <- function(vb_code = NULL, limit = 100,
#' @rdname vb_get
#' @export
vb_get_plot_observations <- function(vb_code = NULL, limit = 100, offset = 0,
parquet = NULL, search = NULL,
parquet = NULL, search = NULL, status = NULL,
detail = NULL, with_nested = NULL,
num_taxa = NULL, num_comms = NULL, ...) {
resource <- "plot-observations"
Expand All @@ -395,7 +410,7 @@ vb_get_plot_observations <- function(vb_code = NULL, limit = 100, offset = 0,
if (missing(with_nested)) with_nested <- if (is_collection) FALSE else TRUE
if (missing(num_taxa)) num_taxa <- if (is_collection) 5 else 5000
if (missing(num_comms)) num_comms <- if (is_collection) 5 else 5000
vb_get(resource, vb_code, parquet = parquet, search = search,
vb_get(resource, vb_code, parquet = parquet, search = search, status = status,
detail = detail, with_nested = with_nested, num_taxa = num_taxa,
num_comms = num_comms, limit = limit, offset = offset, ...)
}
Expand Down
Loading
Loading