@@ -111,19 +111,23 @@ print.epidata_call <- function(epidata_call) {
111111# ' time_value and value fields or c("-direction") to return everything except
112112# ' the direction field
113113# ' @param disable_date_parsing disable automatic date parsing
114+ # ' @param return_empty boolean that allows returning an empty tibble if there is no data
115+ # ' @param timeout_seconds the maximum amount of time to wait for a response
114116# ' @return
115117# ' - For `fetch`: a tibble or a JSON-like list
116118# ' @export
117119# '
118- fetch <- function (epidata_call , fields = NULL , disable_date_parsing = FALSE ) {
120+ fetch <- function (epidata_call , fields = NULL , disable_date_parsing = FALSE , return_empty = FALSE , timeout_seconds = 30 ) {
119121 stopifnot(inherits(epidata_call , " epidata_call" ))
120122 stopifnot(is.null(fields ) || is.character(fields ))
121123 stopifnot(is.logical(disable_date_parsing ), length(disable_date_parsing ) == 1 )
124+ stopifnot(is.logical(return_empty ))
125+ stopifnot(is.numeric(timeout_seconds ))
122126
123127 if (epidata_call $ only_supports_classic ) {
124- return (fetch_classic(epidata_call , fields ))
128+ return (fetch_classic(epidata_call , fields , return_empty = return_empty , timeout_seconds = timeout_seconds ))
125129 } else {
126- return (fetch_tbl(epidata_call , fields , disable_date_parsing ))
130+ return (fetch_tbl(epidata_call , fields , disable_date_parsing , return_empty , timeout_seconds = timeout_seconds ))
127131 }
128132}
129133
@@ -136,17 +140,21 @@ fetch <- function(epidata_call, fields = NULL, disable_date_parsing = FALSE) {
136140# ' time_value and value fields or c("-direction") to return everything except
137141# ' the direction field
138142# ' @param disable_date_parsing disable automatic date parsing
143+ # ' @param return_empty boolean that allows returning an empty tibble if there is no data.
144+ # ' @param timeout_seconds the maximum amount of time to wait for a response
139145# ' @importFrom readr read_csv
140146# ' @importFrom httr stop_for_status content
141147# ' @importFrom rlang abort
142148# ' @return
143149# ' - For `fetch_tbl`: a [`tibble::tibble`]
144150# ' @importFrom tibble as_tibble
145151# ' @keywords internal
146- fetch_tbl <- function (epidata_call , fields = NULL , disable_date_parsing = FALSE ) {
152+ fetch_tbl <- function (epidata_call , fields = NULL , disable_date_parsing = FALSE , return_empty = FALSE , timeout_seconds = 30 ) {
147153 stopifnot(inherits(epidata_call , " epidata_call" ))
148154 stopifnot(is.null(fields ) || is.character(fields ))
149155 stopifnot(is.logical(disable_date_parsing ), length(disable_date_parsing ) == 1 )
156+ stopifnot(is.logical(return_empty ))
157+ stopifnot(is.numeric(timeout_seconds ))
150158
151159 if (epidata_call $ only_supports_classic ) {
152160 rlang :: abort(" This endpoint only supports the classic message format, due to a non-standard behavior. Use fetch_classic instead." ,
@@ -155,7 +163,10 @@ fetch_tbl <- function(epidata_call, fields = NULL, disable_date_parsing = FALSE)
155163 )
156164 }
157165
158- response_content <- fetch_classic(epidata_call , fields , disable_data_frame_parsing = FALSE )
166+ response_content <- fetch_classic(epidata_call , fields , disable_data_frame_parsing = FALSE , return_empty = return_empty , timeout_seconds = timeout_seconds )
167+ if (return_empty && length(response_content ) == 0 ) {
168+ return (tibble())
169+ }
159170 return (parse_data_frame(epidata_call , response_content , disable_date_parsing ) %> % as_tibble())
160171}
161172
@@ -172,48 +183,38 @@ fetch_tbl <- function(epidata_call, fields = NULL, disable_date_parsing = FALSE)
172183# ' @param disable_data_frame_parsing do not automatically cast the epidata
173184# ' output to a data frame (some endpoints return a list of lists, which is not
174185# ' a data frame)
186+ # ' @param return_empty boolean that allows returning an empty tibble if there is no data.
187+ # ' @param timeout_seconds the maximum amount of time to wait for a response
175188# ' @importFrom httr stop_for_status content http_error
176189# ' @importFrom jsonlite fromJSON
177190# ' @return
178191# ' - For `fetch_classic`: a JSON-like list
179192# ' @keywords internal
180- fetch_classic <- function (epidata_call , fields = NULL , disable_data_frame_parsing = TRUE ) {
193+ fetch_classic <- function (epidata_call , fields = NULL , disable_data_frame_parsing = TRUE , return_empty = FALSE , timeout_seconds = 30 ) {
181194 stopifnot(inherits(epidata_call , " epidata_call" ))
182195 stopifnot(is.null(fields ) || is.character(fields ))
196+ stopifnot(is.logical(return_empty ))
197+ stopifnot(is.numeric(timeout_seconds ))
183198
184- response <- request_impl(epidata_call , " classic" , fields )
199+ response <- request_impl(epidata_call , " classic" , fields , timeout_seconds )
185200 response_content <- httr :: content(response , as = " text" , encoding = " UTF-8" )
186201
187- # TODO Temporary workaround the first row of the response being a comment
188- # Remove on 2023-06-21
189- if (grepl(" This request exceeded" , response_content ) && ! epidata_call $ only_supports_classic ) {
190- response_content <- jsonlite :: fromJSON(response_content , simplifyDataFrame = FALSE )
191- message <- response_content $ epidata [[1L ]]
192- cli :: cli_abort(c(
193- " epidata warning, promoted to error: {message}" ,
194- " i" = " Either:" ,
195- " *" = " set the environment variable DELPHI_EPIDATA_KEY, or" ,
196- " *" = ' set the option "delphi.epidata.key":' ,
197- " " = ' {.code options(delphi.epidata.key = "YOUR_KEY_OR_TEMP_KEY")}' ,
198- " To save your key for later sessions (and hide it from your code), you can edit your .Renviron file with:" ,
199- " *" = " usethis::edit_r_environ()"
200- ))
201- }
202-
203202 response_content <- jsonlite :: fromJSON(response_content , simplifyDataFrame = ! disable_data_frame_parsing )
204203
205204 # success is 1, no results is -2, truncated is 2, -1 is generic error
206205 if (response_content $ result != 1 ) {
207- rlang :: abort(paste0(" epidata error: " , response_content $ message ), " epidata_error" )
206+ if ((response_content $ result != - 2 ) && ! (return_empty )) {
207+ rlang :: abort(paste0(" epidata error: " , response_content $ message ), " epidata_error" )
208+ }
208209 }
209210 if (response_content $ message != " success" ) {
210211 rlang :: warn(paste0(" epidata warning: " , response_content $ message ), " epidata_warning" )
211212 }
212213 return (response_content $ epidata )
213214}
214215
215- fetch_debug <- function (epidata_call , format_type = " classic" , fields = NULL ) {
216- response <- request_impl(epidata_call , format_type , fields )
216+ fetch_debug <- function (epidata_call , format_type = " classic" , fields = NULL , timeout_seconds = 30 ) {
217+ response <- request_impl(epidata_call , format_type , fields , timeout_seconds )
217218 content <- httr :: content(response , " text" , encoding = " UTF-8" )
218219 content
219220}
@@ -263,13 +264,13 @@ with_base_url <- function(epidata_call, base_url) {
263264# ' HTTP errors and forwarding the HTTP body in R errors
264265# ' @importFrom httr stop_for_status content http_type
265266# ' @importFrom xml2 read_html xml_find_all xml_text
266- request_impl <- function (epidata_call , format_type , fields = NULL ) {
267+ request_impl <- function (epidata_call , format_type , fields = NULL , timeout_seconds = 30 ) {
267268 stopifnot(inherits(epidata_call , " epidata_call" ))
268269 stopifnot(format_type %in% c(" json" , " csv" , " classic" ))
269270
270271 url <- full_url(epidata_call )
271272 params <- request_arguments(epidata_call , format_type , fields )
272- response <- do_request(url , params )
273+ response <- do_request(url , params , timeout_seconds )
273274
274275 if (response $ status_code != 200 ) {
275276 # 500, 429, 401 are possible
0 commit comments