Skip to content

Commit

Permalink
Add entity_create
Browse files Browse the repository at this point in the history
* Fix #156: entitylist_update family changed to entity-management
  • Loading branch information
florianm committed Jul 23, 2024
1 parent e2b30f5 commit 29474d5
Show file tree
Hide file tree
Showing 13 changed files with 556 additions and 8 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export(encryption_key_list)
export(enexpr)
export(enquo)
export(ensym)
export(entity_create)
export(entity_detail)
export(entity_list)
export(entity_update)
Expand Down
184 changes: 184 additions & 0 deletions R/entity_create.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#' Creates exactly one Entity in the Dataset.
#'
#' `r lifecycle::badge("experimental")`
#'
#' ### Creating a single Entity
#'
#' For creating a single Entity, include
#'
#' - An optional `uuid`. If skipped, Central will create a UUID for the Entity.
#' - The Entity `label` must be non-empty.
#' - A named list `data` representing the Entity's fields. The value type of
#' all properties is string.
#'
#' `uuid = "..."`
#' `label = "John Doe"`
#' `data = list("firstName" = "John", "age" = "22")`
#'
#' This translates to JSON
#'
#' `{ "label": "John Doe", "data": { "firstName": "John", "age": "22" } }`
#'
#' ### Creating multiple Entities
#'
#' For creating multiple Entities in bulk, the request body takes an array
#' entities containing a list of Entity objects as described above.
#' The bulk entity version also takes a source property with a required name
#' field and optional size, for example to capture the file name and size of a
#' bulk upload source (in MB).
#'
#' ```
#' data=list(
#' "entities" = c(
#' list("label" = "Entity 1", "data" = list("field1" = "value1")),
#' list("label" = "Entity 2", "data" = list("field1" = "value2"))
#' ),
#' "source" = list("name" = "file.csv", "size" = 100)
#' )
#' ```
#'
#' This translates to JSON
#'
#' `{ "entities": [...], "source": {"name": "file.csv", "size": 100} }`
#'
#'
#' You can provide `notes` to store the metadata about the request.
#' The metadata is included in the POST request as header `X-Action-Notes` and
#' can retrieved using Entity Audit Log.
#'
#'
#' @template tpl-structure-nested
#' @template tpl-names-cleaned-top-level
#' @template tpl-auth-missing
#' @template tpl-compat-2022-3
#' @template param-pid
#' @template param-did
#' @param label (character) The Entity label which must be a non-empty string.
#' Default: `""`.
#' @param notes (character) Metadata about the request which can be retrieved
#' using the entity audit log.
#' Default: `""`.
#' @param data (list) A named list of Entity properties to create a single
#' Entity, or a nested list with an array of Entity data to create multiple
#' Entities. See details for the exact format.
#' Default: `list()`.
#' @template param-url
#' @template param-auth
#' @template param-retries
#' @template param-odkcv
#' @template param-orders
#' @template param-tz
#' @return A nested list identical to the return value of `entity_detail`.
#' See <https://docs.getodk.org/central-api-entity-management/#creating-entities>
#' for the full schema.
#' Top level list elements are renamed from ODK's `camelCase` to `snake_case`.
#' Nested list elements have the original `camelCase`.
# nolint start
#' @seealso \url{https://docs.getodk.org/central-api-entity-management/#creating-entities}
# nolint end
#' @family entity-management
#' @export
#' @examples
#' \dontrun{
#' # See vignette("setup") for setup and authentication options
#' # ruODK::ru_setup(svc = "....svc", un = "[email protected]", pw = "...")
#'
#' el <- entitylist_list()
#'
#' # Entity List name (dataset ID, did)
#' did <- el$name[1]
#'
#' # All Entities of Entity List
#' en <- entity_list(did = did)
#'
#' # Create a single entity
#' ec <- entity_create(
#' did = did,
#' label = "Entity label",
#' notes = "Metadata about the created entity",
#' data = list("field1" = "value1", "field2" = "value1")
#' )
#' ec
#'
#' # Create multiple entities
#' eb <- entity_create(
#' did = did,
#' notes = "Metadata about the created entities",
#' data = list(
#' "entities" = c(
#' list("label" = "Entity 1", "data" = list("field1" = "value1")),
#' list("label" = "Entity 2", "data" = list("field1" = "value2"))
#' ),
#' "source" = list("name" = "file.csv", "size" = 100)
#' )
#' )
#' eb
#' }
entity_create <- function(pid = get_default_pid(),
did = "",
label = "",
uuid = NULL,
notes = "",
data = list(),
url = get_default_url(),
un = get_default_un(),
pw = get_default_pw(),
retries = get_retries(),
odkc_version = get_default_odkc_version(),
orders = c(
"YmdHMS",
"YmdHMSz",
"Ymd HMS",
"Ymd HMSz",
"Ymd",
"ymd"
),
tz = get_default_tz()) {
yell_if_missing(url, un, pw, pid = pid, did = did)

if (odkc_version |> semver_lt("2022.3")) {
ru_msg_warn("entity_create is supported from v2022.3")
}

# Triage whether data is single or multiple entity
body <- list()

if (label == "") {
ru_msg_info("Empty label: creating multiple entities.")

# Gate check: data must have key "entities"
if (!("entities" %in% names(data))) {
ru_msg_abort("Malformed argument 'data': Must include 'entities'.")
}

body <- data
} else {
ru_msg_info("Non-empty label: creating single entity.")
# If uuid is not NULL, include in body
if (!is.null(uuid)) {
body <- list("uuid" = uuid, "label" = label, "data" = data)
} else {
body <- list("label" = label, "data" = data)
}
}

pth <- glue::glue(
"v1/projects/{pid}/datasets/{URLencode(did, reserved = TRUE)}/",
"entities/"
)

httr::RETRY(
"POST",
httr::modify_url(url, path = pth),
httr::add_headers("Accept" = "application/json", "X-Action-Notes" = notes),
encode = "json",
body = body,
httr::authenticate(un, pw),
times = retries
) |>
yell_if_error(url, un, pw) |>
httr::content(encoding = "utf-8") |>
janitor::clean_names()
}

# usethis::use_test("entity_create") # nolint
2 changes: 1 addition & 1 deletion R/entitylist_update.R
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
# nolint start
#' @seealso \url{ https://docs.getodk.org/central-api-dataset-management/#datasets}
# nolint end
#' @family dataset-management
#' @family entity-management
#' @export
#' @examples
#' \dontrun{
Expand Down
1 change: 1 addition & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
home:
title: An R Client for the ODK Central API
description: R, you, and ODK - Interact with ODK Central from R
url: https://docs.ropensci.org/ruODK/
template:
opengraph:
image:
Expand Down
Loading

0 comments on commit 29474d5

Please sign in to comment.