Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add input check functions using {cli} #150

Merged
merged 10 commits into from
Feb 20, 2024
92 changes: 63 additions & 29 deletions R/add_item.R
Original file line number Diff line number Diff line change
Expand Up @@ -81,48 +81,38 @@ add_item <- function(
# fetch the host from the token
host <- token[["arcgis_host"]]

# if async = TRUE stop
# type must be feature service right now
# TODO make this cli_abort()
stopifnot(
"`async` must be `FALSE`" = !async,
"`type` must be `\"Feature Service\"`" = identical(type, "Feature Service")
check_add_item_args(
description = description,
snippet = snippet,
async = async,
type = type
)

# if CRS is missing require user input if interactive
if (interactive() && is.na(sf::st_crs(x)) && inherits(x, "sf")) {
cli::cli_bullets(c("!" = "{.arg x} has no CRS"))

choice <- utils::menu(
c("Yes", "No"),
title = "CRS is missing from `x`. Continue?"
)

if (choice == 2L) {
# TODO cli_abort
stop("Aborting. CRS is missing.")
cli::cli_abort("Aborting.")
} else {
# TODO cli_warn
warning("Set the CRS to prevent this interruption.\n - use `sf::st_set_crs()`")
cli::cli_warn(
c("{.arg x} has no CRS.",
"*" = "Set CRS with {.fn sf::st_set_crs}")
)
}

} else if (!interactive() && is.na(sf::st_crs(x))) {
# TODO cli_warn
warning(
"CRS is missing from `x`\nAssuming EPSG:3857."
cli::cli_warn(
c("CRS is missing from {.arg x}",
"i" = "Using {.val EPSG:3857}")
)
}

# check if snippet is too long
# TODO cli_warn
if (nchar(snippet) > 2048) warning("Snippet must be 2048 or fewer characters.")

# check if description is too big or too many eles
descrip_kb <- as.numeric(utils::object.size(description)) / 1000

# TODO cli_abort
stopifnot(
"`description` must be smaller than 64kb" = descrip_kb <= 64,
"`description` must be length 1" = length(description) == 1
)

req_url <- paste0(host, "/sharing/rest/content/users/", user, "/addItem")

# create the feature collection json
Expand Down Expand Up @@ -167,6 +157,50 @@ add_item <- function(
data.frame(parsed)
}

#' @noRd
check_add_item_args <- function(
description = "",
snippet = "",
async = FALSE,
type = "Feature Service",
call = rlang::caller_env()) {

# if async = TRUE stop
if (async) {
cli::cli_abort(
"{.arg async} must be {.val FALSE}",
call = call
)
}

# type must be feature service right now
if (!identical(type, "Feature Service")) {
check_string(type, call = call)
cli::cli_abort(
"{.arg type} must be {.str Feature Service}",
call = call
)
}

# TODO Check if snippet is allowed as a NULL input
check_string(snippet, call = call)

# check if snippet is too long
if (nchar(snippet) > 2048) {
# TODO If snippet *must* be 2048 or fewer characters this should be an error
cli::cli_warn("{.arg snippet} must be 2048 or fewer characters.")
}

check_string(description, call = call)
# check if description is too big or too many eles
descrip_kb <- as.numeric(utils::object.size(description)) / 1000

if (descrip_kb > 64) {
cli::cli_abort(
"{.arg description} must be smaller than 64kb"
)
}
}


#' @export
Expand Down Expand Up @@ -283,9 +317,9 @@ publish_layer <- function(
# - layerInfo (ignore for now. No good use case)
# - targetSR (derive from the object)

check_null_or_scalar(name)
check_null_or_scalar(description)
check_null_or_scalar(copyright)
check_string(name, allow_null = TRUE)
check_string(description, allow_null = TRUE)
check_string(copyright, allow_null = TRUE)

if (is.na(target_crs)) {
target_sr <- NULL
Expand Down
82 changes: 44 additions & 38 deletions R/arc-add-update-delete.R
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ add_features <- function(
# initial check for type of `x`
obj_check_layer(x)

stopifnot("`.data` must be a data.frame-type class" = inherits(.data, "data.frame"))
if (!rlang::inherits_any(.data, "data.frame")) {
data_name <- deparse(substitute(.data))
cli::cli_abort(
"{.arg {data_name}} must be a {.cls data.frame}."
)
}

match_on <- match.arg(match_on)

Expand All @@ -71,21 +76,7 @@ add_features <- function(
# TODO address data.frame objects / table layers
# TODO error on list columns

target_crs <- sf::st_crs(x)
provided_crs <- sf::st_crs(.data)

# see commentary in `update_features.R`
if (!target_crs == provided_crs) {
if (is.na(sf::st_crs(.data))) {
warning("CRS missing from `.data` assuming ", sf::st_crs(x)$srid)
} else if (is.na(sf::st_crs(x))) {
warning("CRS missing from `x` cannot verify matching CRS.")
} else {
stop("`FeatureLayer` and `.data` have different CRS\nTranform to the same CRS:\n",
" `sf::st_transform(.data, sf::st_crs(x))`")
}
}

check_crs_match(x, .data)

# not that addFeatures does not update layer definitions so if any attributes
# are provided that aren't in the feature layer, they will be ignored
Expand All @@ -110,15 +101,10 @@ add_features <- function(
colnames(.data) <- cnames
}

# columns not in the feature layer
nin_feature <- setdiff(cnames[!present_index], geo_col)

if (length(nin_feature) > 0 ) {
message(
"Columns in `.data` not in feature(s): ",
ifelse(length(nin_feature) > 1, paste0(nin_feature, collapse = ", "), nin_feature)
)
}
inform_nin_feature(
# columns not in the feature layer
setdiff(cnames[!present_index], geo_col)
)

# subset accordingly
.data <- .data[, present_index]
Expand Down Expand Up @@ -204,10 +190,17 @@ update_features <- function(
if (!identical(sf::st_crs(x), sf::st_crs(.data))) {

if (is.na(sf::st_crs(.data)) && inherits(.data, "sf")) {
warning("CRS missing from `.data` assuming ", sf::st_crs(x)$srid)
} else if (inherits(.data, "sf")){
stop("`FeatureLayer` and `.data` have different CRS\nTranform to the same CRS:\n",
" `sf::st_transform(.data, sf::st_crs(x))`")
cli::cli_warn(
c("{.arg data} is missing a CRS",
"i" = paste0("Setting CRS to ", sf::st_crs(x)$srid)
))
} else if (inherits(.data, "sf")) {
cli::cli_abort(
c("{.arg x} and {.arg .data} must share the same CRS",
"*" = "Tranform {.arg .data} to the same CRS as {.arg x} with
{.fn sf::st_transform}"
)
)
}
} # not that addFeatures does not update layer definitions so if any attributes
# are provided that aren't in the feature layer, they will be ignored
Expand All @@ -232,15 +225,10 @@ update_features <- function(
colnames(.data) <- cnames
}

# columns not in the feature layer
nin_feature <- setdiff(cnames[!present_index], geo_col)

if (length(nin_feature) > 0 ) {
message(
"Columns in `.data` not in feature(s): ",
ifelse(length(nin_feature) > 1, paste0(nin_feature, collapse = ", "), nin_feature)
)
}
inform_nin_feature(
# columns not in the feature layer
setdiff(cnames[!present_index], geo_col)
)

# subset accordingly
.data <- .data[, present_index]
Expand All @@ -261,6 +249,24 @@ update_features <- function(
RcppSimdJson::fparse(httr2::resp_body_string(resp))
}

#' @noRd
inform_nin_feature <- function(nin_feature) {
if (length(nin_feature) == 0) {
return(invisible(NULL))
}

cli::cli_inform(
paste0(
"Columns in `.data` not in feature(s): ",
ifelse(
length(nin_feature) > 1,
paste0(nin_feature, collapse = ", "),
nin_feature
)
)
)
}


# Delete Features ---------------------------------------------------------

Expand Down
8 changes: 5 additions & 3 deletions R/arc-open.R
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
#'}
arc_open <- function(url, token = arc_token()) {

stopifnot("`url` must be of length 1" = length(url) == 1)
check_url(url)

# extract layer metadata
meta <- compact(fetch_layer_metadata(url, token))
Expand All @@ -78,7 +78,9 @@ arc_open <- function(url, token = arc_token()) {
} else if ("layers" %in% names(meta) || grepl("FeatureServer", meta[["url"]])) {
layer_class <- "FeatureServer"
} else {
stop("Cannot determine layer type")
cli::cli_abort(
"Can't determine layer type from {.arg url}: {.url {url}}"
)
}
}

Expand All @@ -103,7 +105,7 @@ arc_open <- function(url, token = arc_token()) {
"GroupLayer" = structure(meta, class = layer_class),
cli::cli_abort(
c(
"Unsupported service type",
"Service type {.val {layer_class}} is not supported.",
"i"= "Please report this at {.url https://github.com/R-ArcGIS/arcgislayers/issues}"
)
)
Expand Down
Loading
Loading