Skip to content

Commit 858a7e2

Browse files
committed
added retry logic to HTTP requests
1 parent 693ddbf commit 858a7e2

File tree

9 files changed

+250
-217
lines changed

9 files changed

+250
-217
lines changed

NAMESPACE

+3-3
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,14 @@ importFrom(htmlwidgets,saveWidget)
241241
importFrom(htmlwidgets,shinyRenderWidget)
242242
importFrom(htmlwidgets,shinyWidgetOutput)
243243
importFrom(htmlwidgets,sizingPolicy)
244-
importFrom(httr,GET)
245-
importFrom(httr,PATCH)
246-
importFrom(httr,POST)
244+
importFrom(httr,RETRY)
247245
importFrom(httr,add_headers)
246+
importFrom(httr,authenticate)
248247
importFrom(httr,config)
249248
importFrom(httr,content)
250249
importFrom(httr,stop_for_status)
251250
importFrom(httr,warn_for_status)
251+
importFrom(httr,write_disk)
252252
importFrom(jsonlite,parse_json)
253253
importFrom(jsonlite,read_json)
254254
importFrom(jsonlite,toJSON)

R/api_exports.R

+58-50
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,68 @@
11
#' Tools for working with plotly's REST API (v2)
2-
#'
2+
#'
33
#' Convenience functions for working with version 2 of plotly's REST API.
44
#' Upload R objects to a plotly account via `api_create()` and download
55
#' plotly objects via `api_download_plot()`/`api_download_grid()`.
66
#' For anything else, use `api()`.
7-
#'
8-
#' @param id a filename id.
7+
#'
8+
#' @param id a filename id.
99
#' @param username a plotly username.
10-
#'
11-
#' @param x An R object to hosted on plotly's web platform.
10+
#'
11+
#' @param x An R object to hosted on plotly's web platform.
1212
#' Can be a plotly/ggplot2 object or a \link{data.frame}.
1313
#' @param filename character vector naming file(s). If `x` is a plot,
1414
#' can be a vector of length 2 naming both the plot AND the underlying grid.
15-
#' @param fileopt character string describing whether to "overwrite" existing
15+
#' @param fileopt character string describing whether to "overwrite" existing
1616
#' files or ensure "new" file(s) are always created.
17-
#' @param sharing If 'public', anyone can view this graph. It will appear in
17+
#' @param sharing If 'public', anyone can view this graph. It will appear in
1818
#' your profile and can appear in search engines. You do not need to be
1919
#' logged in to Plotly to view this chart.
2020
#' If 'private', only you can view this plot. It will not appear in the
21-
#' Plotly feed, your profile, or search engines. You must be logged in to
22-
#' Plotly to view this graph. You can privately share this graph with other
23-
#' Plotly users in your online Plotly account and they will need to be logged
21+
#' Plotly feed, your profile, or search engines. You must be logged in to
22+
#' Plotly to view this graph. You can privately share this graph with other
23+
#' Plotly users in your online Plotly account and they will need to be logged
2424
#' in to view this plot.
2525
#' If 'secret', anyone with this secret link can view this chart. It will
26-
#' not appear in the Plotly feed, your profile, or search engines.
27-
#' If it is embedded inside a webpage or an IPython notebook, anybody who is
28-
#' viewing that page will be able to view the graph.
26+
#' not appear in the Plotly feed, your profile, or search engines.
27+
#' If it is embedded inside a webpage or an IPython notebook, anybody who is
28+
#' viewing that page will be able to view the graph.
2929
#' You do not need to be logged in to view this plot.
30-
#'
31-
#' @param endpoint the endpoint (i.e., location) for the request.
30+
#'
31+
#' @param endpoint the endpoint (i.e., location) for the request.
3232
#' To see a list of all available endpoints, call `api()`.
3333
#' Any relevant query parameters should be included here (see examples).
34-
#' @param verb name of the HTTP verb to use (as in, [httr::VERB()]).
35-
#' @param body body of the HTTP request(as in, [httr::VERB()]).
36-
#' If this value is not already converted to JSON
34+
#' @param verb name of the HTTP verb to use (as in, [httr::RETRY()]).
35+
#' @param body body of the HTTP request(as in, [httr::RETRY()]).
36+
#' If this value is not already converted to JSON
3737
#' (via [jsonlite::toJSON()]), it uses the internal `to_JSON()`
3838
#' to ensure values are "automatically unboxed" (i.e., vec.
3939
#'
40-
#' @param ... For `api()`, these arguments are passed onto
41-
#' [httr::VERB()]. For `api_create()`, these arguments are
40+
#' @param ... For `api()`, these arguments are passed onto
41+
#' [httr::RETRY()]. For `api_create()`, these arguments are
4242
#' included in the body of the HTTP request.
43-
#'
43+
#'
4444
#' @export
4545
#' @rdname api
4646
#' @author Carson Sievert
4747
#' @references \url{https://api.plot.ly/v2}
4848
#' @seealso [signup()]
49-
#' @examples
50-
#'
49+
#' @examples
50+
#'
5151
#' \dontrun{
52-
#'
52+
#'
5353
#' # ------------------------------------------------------------
5454
#' # api_create() makes it easy to upload ggplot2/plotly objects
5555
#' # and/or data frames to your plotly account
5656
#' # ------------------------------------------------------------
57-
#'
58-
#' # A data frame creates a plotly "grid". Printing one will take you
57+
#'
58+
#' # A data frame creates a plotly "grid". Printing one will take you
5959
#' # to the it's web address so you can start creating!
6060
#' (m <- api_create(mtcars))
61-
#'
61+
#'
6262
#' # A plotly/ggplot2 object create a plotly "plot".
6363
#' p <- plot_ly(mtcars, x = ~factor(vs))
6464
#' (r <- api_create(p))
65-
#'
65+
#'
6666
#' # api_create() returns metadata about the remote "file". Here is
6767
#' # one way you could use that metadata to download a plot for local use:
6868
#' fileID <- strsplit(r$file$fid, ":")[[1]]
@@ -72,18 +72,18 @@
7272
#' )
7373
#'
7474
#' ------------------------------------------------------------
75-
#' # The api() function provides a low-level interface for performing
75+
#' # The api() function provides a low-level interface for performing
7676
#' # any action at any endpoint! It always returns a list.
7777
#' # ------------------------------------------------------------
78-
#'
78+
#'
7979
#' # list all the endpoints
8080
#' api()
81-
#'
81+
#'
8282
#' # search the entire platform!
8383
#' # see https://api.plot.ly/v2/search
8484
#' api("search?q=overdose")
8585
#' api("search?q=plottype:pie trump fake")
86-
#'
86+
#'
8787
#' # these examples will require a user account
8888
#' usr <- Sys.getenv("plotly_username", NA)
8989
#' if (!is.na(usr)) {
@@ -92,27 +92,27 @@
9292
#' # your folders/files https://api.plot.ly/v2/folders#user
9393
#' api(sprintf("folders/home?user=%s", usr))
9494
#' }
95-
#'
95+
#'
9696
#' # Retrieve a specific file https://api.plot.ly/v2/files#retrieve
9797
#' api("files/cpsievert:14681")
98-
#'
98+
#'
9999
#' # change the filename https://api.plot.ly/v2/files#update
100100
#' # (note: this won't work unless you have proper credentials to the relevant account)
101-
#' api("files/cpsievert:14681", "PATCH", list(filename = "toy file"))
102-
#'
101+
#' api("files/cpsievert:14681", "PATCH", list(filename = "toy file"))
102+
#'
103103
#' # Copy a file https://api.plot.ly/v2/files#lookup
104104
#' api("files/cpsievert:14681/copy", "POST")
105-
#'
105+
#'
106106
#' # Create a folder https://api.plot.ly/v2/folders#create
107107
#' api("folders", "POST", list(path = "/starts/at/root/and/ends/here"))
108-
#'
108+
#'
109109
#' }
110-
#'
110+
#'
111111

112112

113113
#' @rdname api
114114
#' @export
115-
api_create <- function(x = last_plot(), filename = NULL,
115+
api_create <- function(x = last_plot(), filename = NULL,
116116
fileopt = c("overwrite", "new"),
117117
sharing = c("public", "private", "secret"), ...) {
118118
fileopt <- match.arg(fileopt, c("overwrite", "new"))
@@ -140,7 +140,7 @@ api_create.data.frame <- api_create_grid
140140
api_download_plot <- function(id, username) {
141141
f <- api_download_file(id, username)
142142
api_expect_filetype(f, "plot")
143-
143+
144144
as_widget(
145145
api_download_file(id, username, "plots", "content?inline_data=true")
146146
)
@@ -152,7 +152,7 @@ api_download_plot <- function(id, username) {
152152
api_download_grid <- function(id, username) {
153153
f <- api_download_file(id, username)
154154
api_expect_filetype(f, "grid")
155-
155+
156156
prefix_class(
157157
api_download_file(id, username, "grids"), "api_grid_local"
158158
)
@@ -170,27 +170,35 @@ api_download_file <- function(id, username, endpoint = "files", ...) {
170170

171171

172172
#' @rdname api
173+
#' @importFrom httr RETRY
173174
#' @export
174175
api <- function(endpoint = "/", verb = "GET", body = NULL, ...) {
175176
api_check_endpoint(endpoint)
176-
177+
177178
# construct the url
178179
url <- httr::modify_url(
179-
get_domain("api"),
180+
get_domain("api"),
180181
scheme = "https",
181182
# TODO: should anything else in the endpoint (besides whitespace) be escaped?
182183
path = file.path("v2", gsub("\\s+", "+", endpoint))
183184
)
184-
185+
185186
# default to unboxing (i.e., no arrays of length 1)
186187
if (!is.null(body) && !inherits(body, "json")) {
187188
body <- to_JSON(body)
188189
}
189-
190-
resp <- httr::VERB(
191-
verb = verb, url = url, api_headers(), api_auth(),
192-
body = body, ...
190+
191+
resp <- httr::RETRY(
192+
verb = verb
193+
, url = url
194+
, api_headers()
195+
, api_auth()
196+
, body = body
197+
, times = 5
198+
, terminate_on = c(400, 401, 403, 404)
199+
, terminate_on_success = TRUE
200+
, ...
193201
)
194-
202+
195203
structure(process(resp), class = "api")
196204
}

R/imports.R

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#' @importFrom tidyr unnest
77
#' @importFrom viridisLite viridis
88
#' @importFrom jsonlite toJSON parse_json read_json
9-
#' @importFrom httr GET POST PATCH content config add_headers stop_for_status warn_for_status
9+
#' @importFrom httr RETRY content config add_headers authenticate stop_for_status warn_for_status write_disk
1010
#' @importFrom htmlwidgets createWidget sizingPolicy saveWidget onRender prependContent
1111
#' @importFrom lazyeval f_eval is_formula all_dots is_lang f_new
1212
#' @importFrom tibble as_tibble
@@ -18,7 +18,7 @@
1818
NULL
1919

2020

21-
#' @importFrom dplyr mutate
21+
#' @importFrom dplyr mutate
2222
#' @name mutate
2323
#' @rdname reexports
2424
#' @export
@@ -66,7 +66,7 @@ dplyr::rename
6666
#' @export
6767
dplyr::rename_
6868

69-
#' @importFrom dplyr group_by
69+
#' @importFrom dplyr group_by
7070
#' @name group_by
7171
#' @rdname reexports
7272
#' @export
@@ -169,7 +169,7 @@ dplyr::filter_
169169
# #' @rdname reexports
170170
# #' @export
171171
# tidyr::gather
172-
#
172+
#
173173
# #' @importFrom tidyr gather_
174174
# #' @name gather_
175175
# #' @rdname reexports

0 commit comments

Comments
 (0)