Skip to content

Commit

Permalink
Merge pull request #143 from R-ArcGIS/basereq
Browse files Browse the repository at this point in the history
Basereq
  • Loading branch information
JosiahParry authored Feb 9, 2024
2 parents cc48616 + 24312f4 commit 0edd537
Show file tree
Hide file tree
Showing 26 changed files with 109 additions and 243 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Imports:
terra,
utils
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.0
RoxygenNote: 7.3.1
Suggests:
dbplyr,
dplyr,
Expand Down
4 changes: 3 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# arcgislayers 0.1.0 (unreleased)

- **Breaking**:
- `token` arguments are required to be a valid `httr2_token` object (strings are not supported).
- all `host` arguments are removed. Instead, the host is fetched from the `token`.
- Add support for `GroupLayer`s
- Add `arc_read()` with support for `name_repair` argument using `{vctrs}` (#108)
- Add `get_layer_estimates()` to retrieve estimate info such as the number of features and the extent of the layer
Expand All @@ -14,4 +17,3 @@
- adds cli as an explicit import (has been implicitly imported by httr2)
- repository made public
- add lifecycle badges to all exported functions <https://github.com/R-ArcGIS/arcgislayers/pull/101>
- Use `arcgisutils::arc_token()` to get "ARCGIS_TOKEN" environment variable. This ensures that empty strings do not cause HTTP 498 "invalid token" error.
41 changes: 27 additions & 14 deletions R/add_item.R
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
#' nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"))
#' x <- nc[1:5, 13]
#'
#' tkn <- auth_code()
#' set_auth_token(tkn)
#' token <- auth_code()
#' set_arc_token(tkn)
#'
#' publish_res <- publish_layer(
#' x, "North Carolina SIDS sample"
Expand All @@ -68,12 +68,19 @@ add_item <- function(
categories = character(0),
async = FALSE,
type = "Feature Service",
host = arc_host(),
token = arc_token()
) {


# validate the token
obj_check_token(token)

# 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")
Expand All @@ -87,22 +94,27 @@ add_item <- function(
)

if (choice == 2L) {
# TODO cli_abort
stop("Aborting. CRS is missing.")
} else {
# TODO cli_warn
warning("Set the CRS to prevent this interruption.\n - use `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."
)
}

# 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
Expand Down Expand Up @@ -139,20 +151,16 @@ add_item <- function(
categories = categories,
type = "Feature Collection",
async = async,
token = token,
url = host,
f = "json"
)
)


req <- httr2::request(req_url)
req <- arc_base_req(req_url, token)
req_body <- httr2::req_body_form(req, !!!req_fields)
resp <- httr2::req_perform(req_body)

parsed <- RcppSimdJson::fparse(httr2::resp_body_string(resp))
detect_errors(parsed)

data.frame(parsed)
}

Expand All @@ -170,22 +178,30 @@ publish_item <- function(
user = Sys.getenv("ARCGIS_USER"),
publish_params = .publish_params(),
file_type = "featureCollection",
host = arc_host(),
token = arc_token()
) {

# validate the token
obj_check_token(token)

# fetch the host
host <- token[["arcgis_host"]]

# create request URL
# TODO check for trailing `/` in host (should create a `sanitize_host()`)
req_url <- paste0(host, "/sharing/rest/content/users/", user, "/publish")

# add token and agent
base_req <- arc_base_req(req_url, token)


# create request
req <- httr2::req_body_form(
httr2::request(req_url),
base_req,
itemID = item_id,
fileType = file_type,
publishParameters = jsonify::to_json(publish_params, unbox = TRUE),
f = "json",
token = token
)

resp <- httr2::req_perform(req)
Expand All @@ -207,7 +223,6 @@ publish_layer <- function(
...,
user = Sys.getenv("ARCGIS_USER"),
publish_params = .publish_params(title, target_crs = sf::st_crs(x)),
host = arc_host(),
token = arc_token()
) {

Expand All @@ -218,7 +233,6 @@ publish_layer <- function(
x,
title,
user = user,
host = host,
token = token,
!!!adtl_args
)
Expand All @@ -232,7 +246,6 @@ publish_layer <- function(
item_id,
user = user,
publish_params = publish_params,
host = host,
token = token
)

Expand Down
20 changes: 7 additions & 13 deletions R/arc-add-update-delete.R
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ add_features <- function(
indices <- chunk_indices(n, chunk_size)

# create the base request from the feature url
req <- httr2::req_url_path_append(httr2::request(x[["url"]]), "addFeatures")
base_req <- arc_base_req(x[["url"]], token)
req <- httr2::req_url_path_append(base_req, "addFeatures")

# pre-allocate list
all_reqs <- vector("list", length = lengths(indices)[1])
Expand All @@ -144,7 +145,6 @@ add_features <- function(
req,
features = as_esri_features(.data[start:end,]),
rollbackOnFailure = rollback_on_failure,
token = token,
f = "json"
)
}
Expand Down Expand Up @@ -246,22 +246,19 @@ update_features <- function(
.data <- .data[, present_index]

# create base request
req <- httr2::request(
paste0(x[["url"]], "/updateFeatures")
)
req <- arc_base_req(paste0(x[["url"]], "/updateFeatures"), token)

req <- httr2::req_body_form(
req,
# transform `.data`
features = as_esri_features(.data),
rollbackOnFailure = rollback_on_failure,
token = token,
f = "json",
...
)

resp <- httr2::req_perform(req)
jsonify::from_json(httr2::resp_body_string(resp))
RcppSimdJson::fparse(httr2::resp_body_string(resp))
}


Expand All @@ -281,9 +278,7 @@ update_features <- function(
#' @inheritParams prepare_spatial_filter
#' @param rollback_on_failure default `TRUE`. Specifies whether the edits should be
#' applied only if all submitted edits succeed.
#' @param token your authorization token. By default, token is set to the
#' environment variable `ARCGIS_TOKEN`. Use `set_auth_token()` to set
#' `ARCGIS_TOKEN`.
#' @param token default `arc_token()`. An `httr2_token`.
#' @export
#' @rdname modify
delete_features <- function(
Expand Down Expand Up @@ -327,21 +322,20 @@ delete_features <- function(
}

# https://developers.arcgis.com/rest/services-reference/enterprise/delete-features.htm
req <- httr2::request(paste0(x[["url"]], "/deleteFeatures"))
req <- arc_base_req(paste0(x[["url"]], "/deleteFeatures"), token)

req <- httr2::req_body_form(
req,
!!!(compact(list(where = where, objectIds = object_ids))),
!!!filter_geom,
f = "json",
token = token,
rollbackOnFailure = rollback_on_failure,
...
)

resp <- httr2::req_perform(req)

jsonify::from_json(httr2::resp_body_string(resp))
RcppSimdJson::fparse(httr2::resp_body_string(resp))
}


16 changes: 10 additions & 6 deletions R/arc-open.R
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,11 @@ arc_open <- function(url, token = arc_token()) {

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

# generate base request
req <- httr2::request(url)

# extract layer metadata
meta <- compact(fetch_layer_metadata(req, token))
meta[["url"]] <- url # set url for later use
meta <- compact(fetch_layer_metadata(url, token))

# set url for later use
meta[["url"]] <- url

# layer class
layer_class <- gsub("\\s", "", meta[["type"]])
Expand Down Expand Up @@ -103,7 +102,12 @@ arc_open <- function(url, token = arc_token()) {
"ImageServer" = structure(meta, class = layer_class),
"MapServer" = structure(meta, class = layer_class),
"GroupLayer" = structure(meta, class = layer_class),
cli::cli_abort("Unsupported service")
cli::cli_abort(
c(
"Unsupported service type",
"i"= "Please report this at {.url https://github.com/R-ArcGIS/arcgislayers/issues}"
)
)
)

res
Expand Down
11 changes: 7 additions & 4 deletions R/arc-raster.R
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,26 @@ arc_raster <- function(
# if bbox_crs is missing set to `crs`
bbox_sr <- validate_crs(bbox_crs %||% crs)[[c("spatialReference", "wkid")]]

req <- httr2::request(paste0(x[["url"]], "/exportImage"))
# create the base req
burl <- paste0(x[["url"]], "/exportImage")

# pass that into arc_base_req() to set agent and token
req <- arc_base_req(burl, token)

req <- httr2::req_body_form(
req,
# bbox = paste0(bbox, collapse = ","),
bbox = paste0(c(xmin, ymin, xmax, ymax), collapse = ","),
bboxSR = bbox_sr,
format = format,
size = paste0(c(width, height), collapse = ","),
token = token,
outSR = out_sr,
f = "json"
)

# fetch the response
resp <- httr2::req_perform(req)

resp_meta <- jsonify::from_json(httr2::resp_body_string(resp))
resp_meta <- RcppSimdJson::fparse(httr2::resp_body_string(resp))

detect_errors(resp_meta)

Expand Down
30 changes: 12 additions & 18 deletions R/arc-select.R
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ arc_select <- function(
token = arc_token(),
...
) {

# Developer note:
# For this function we extract the query object and manipulate the elements
# inside of the query object to modify our request. We then splice those
Expand Down Expand Up @@ -135,20 +136,16 @@ collect_layer <- function(x,
token = arc_token(),
...,
error_call = rlang::caller_env()) {
check_inherits_any(
x,
c("FeatureLayer", "Table", "ImageServer"),
call = error_call
)

# 1. Make base request
# 2. Identify necessary query parameters
# 3. Figure out offsets and update query parameters
# 4. Make list of requests
# 5. Make requests
# 6. Identify errors (if any) -- skip for now
# 7. Parse:
req <- httr2::request(x[["url"]])

# sets token and agent
req <- arc_base_req(x[["url"]], token)

# determine if the layer can query
can_query <- switch(
Expand Down Expand Up @@ -177,13 +174,14 @@ collect_layer <- function(x,

# parameter validation ----------------------------------------------------
# get existing parameters
query_params <- validate_params(query, token = token)
query_params <- validate_params(query)

# Offsets -----------------------------------------------------------------
# TODO make adjustable
feats_per_page <- x[["maxRecordCount"]]

# count the number of features in a query
n_feats <- count_results(req, query, token)
n_feats <- count_results(req, query)

if (is.null(n_feats)) {
cli::cli_abort(
Expand Down Expand Up @@ -215,13 +213,11 @@ collect_layer <- function(x,
all_requests <- lapply(offsets, add_offset, req, query_params)

# make all requests and store responses in list
all_resps <- httr2::req_perform_parallel(all_requests)
all_resps <- httr2::req_perform_parallel(all_requests, on_error = "continue")

# identify any errors
has_error <- vapply(all_resps, function(x) inherits(x, "error"), logical(1))
# if (any(has_error)) {
# TODO: determine how to handle errors
# }
has_error <- vapply(all_resps, function(x) inherits(x, "error"), logical(1))

# fetch the results
res <- lapply(
Expand Down Expand Up @@ -357,9 +353,7 @@ add_offset <- function(offset, request, params) {
#'
#' @keywords internal
#' @noRd
validate_params <- function(params, token) {
# set the token
params[["token"]] <- token
validate_params <- function(params) {

# if output fields are missing set to "*"
if (is.null(params[["outFields"]])) params[["outFields"]] <- "*"
Expand All @@ -378,10 +372,10 @@ validate_params <- function(params, token) {
}

# Given a query, determine how many features will be returned
count_results <- function(req, query, token) {
count_results <- function(req, query) {
n_req <- httr2::req_body_form(
httr2::req_url_path_append(req, "query"),
!!!validate_params(query, token),
!!!validate_params(query),
returnCountOnly = "true"
)

Expand Down
Loading

0 comments on commit 0edd537

Please sign in to comment.