Skip to content

Commit 4d47320

Browse files
committed
Swith to {httr2} to download data and add update tests accordingly using {vcr}
1 parent 869167b commit 4d47320

File tree

10 files changed

+576
-61
lines changed

10 files changed

+576
-61
lines changed

DESCRIPTION

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Imports:
2525
ggplot2,
2626
grid,
2727
gridtext,
28-
jsonlite,
28+
httr2,
2929
lubridate,
3030
ncdf4,
3131
patchwork,
@@ -41,7 +41,8 @@ Suggests:
4141
knitr,
4242
mockery,
4343
rmarkdown,
44-
testthat
44+
testthat,
45+
vcr
4546
LinkingTo:
4647
Rcpp,
4748
RcppArmadillo

R/download_data.R

Lines changed: 88 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111
#' @return a tibble with 2 columns and as many rows as needed
1212
#' @noRd
1313
get_parameters_raw <- function(
14-
parameter = "hs",
15-
node = 42,
16-
start = as.POSIXct("1994-01-01Z00:00:00", tz = "UTC"),
17-
end = as.POSIXct("1994-12-31Z23:00:00", tz = "UTC")) {
14+
parameter = "hs",
15+
node = 42,
16+
start = as.POSIXct("1994-01-01Z00:00:00", tz = "UTC"),
17+
end = as.POSIXct("1994-12-31Z23:00:00", tz = "UTC")
18+
) {
1819
if (parameter == "tp") {
1920
single_parameter <- "fp"
2021
} else {
@@ -27,7 +28,7 @@ get_parameters_raw <- function(
2728
# Cassandra database start indexing at 1, so decrements node number
2829
node <- node - 1
2930

30-
request <- paste0(
31+
request_url <- paste0(
3132
rcd_cassandra_url,
3233
"api/timeseries",
3334
"?parameter=",
@@ -40,28 +41,68 @@ get_parameters_raw <- function(
4041
end_str
4142
)
4243

43-
# Try retrieving and parsing JSON
44+
# Try retrieving and parsing JSON using httr2
45+
resp <- tryCatch(
46+
httr2::request(request_url) |>
47+
httr2::req_error(is_error = \(resp) FALSE) |> # Don't auto-error on HTTP errors
48+
httr2::req_retry(max_tries = 3) |> # Retry transient failures
49+
httr2::req_timeout(30) |> # 30 second timeout
50+
httr2::req_perform(),
51+
httr2_failure = function(cnd) {
52+
message(
53+
"Network error: Could not connect to the remote resource. ",
54+
"The server may be unavailable."
55+
)
56+
NULL
57+
},
58+
error = function(e) {
59+
message("Unexpected error retrieving data: ", conditionMessage(e))
60+
NULL
61+
}
62+
)
63+
64+
# If request failed, exit
65+
if (is.null(resp)) {
66+
return(NULL)
67+
}
68+
69+
# Check HTTP status
70+
if (httr2::resp_status(resp) != 200) {
71+
message(
72+
"HTTP error ",
73+
httr2::resp_status(resp),
74+
": ",
75+
httr2::resp_status_desc(resp)
76+
)
77+
return(NULL)
78+
}
79+
80+
# Parse JSON response
4481
res <- tryCatch(
45-
jsonlite::fromJSON(request),
82+
httr2::resp_body_json(resp, simplifyVector = TRUE),
4683
error = function(e) {
47-
message("Could not retrieve data from the remote resource. ",
48-
"The server may be unavailable or the URL may have changed.")
49-
NULL # graceful fallback
84+
message("Error parsing response: Invalid JSON format")
85+
NULL
5086
}
5187
)
5288

53-
# If retrieval failed, exit
54-
if (is.null(res)) return(NULL)
89+
# If parsing failed, exit
90+
if (is.null(res)) {
91+
return(NULL)
92+
}
5593

5694
# Check API-level error
5795
if (!is.null(res$errorcode) && res$errorcode != 0) {
58-
message("The data source returned an error: ", res$errormessage,
59-
"\nReturning NULL.")
60-
return(NULL) # graceful fallback
96+
message(
97+
"The data source returned an error: ",
98+
res$errormessage,
99+
"\nReturning NULL."
100+
)
101+
return(NULL) # graceful fallback
61102
}
62103

63-
64-
data <- res$result$data
104+
# Convert list to data frame
105+
data <- as.data.frame(res$result$data)
65106
colnames(data) <- c("time", parameter)
66107
data <- tibble::as_tibble(data)
67108

@@ -70,14 +111,15 @@ get_parameters_raw <- function(
70111
}
71112

72113
data$time <- as.POSIXct(
73-
data$time / 1000,
114+
as.numeric(data$time) / 1000,
74115
origin = as.POSIXct("1970-01-01", tz = "UTC"),
75116
tz = "UTC"
76117
) # Convert UNIX time (ms) to POSIXct format
77118
attr(data, "node") <- node
78119
data
79120
}
80121

122+
81123
#' Download time series of sea-state parameters from RESOURCECODE database
82124
#'
83125
#' If the remote resource is unavailable or returns an error, the function returns NULL
@@ -95,17 +137,21 @@ get_parameters_raw <- function(
95137
#' ts <- get_parameters(parameters = c("hs", "tp"), node = 42)
96138
#' plot(ts$time, ts$hs, type = "l")
97139
get_parameters <- function(
98-
parameters = "hs",
99-
node = 42,
100-
start = as.POSIXct("1994-01-01 00:00:00", tz = "UTC"),
101-
end = as.POSIXct("1994-12-31 23:00:00", tz = "UTC")) {
140+
parameters = "hs",
141+
node = 42,
142+
start = as.POSIXct("1994-01-01 00:00:00", tz = "UTC"),
143+
end = as.POSIXct("1994-12-31 23:00:00", tz = "UTC")
144+
) {
102145
parameters <- tolower(parameters)
103146

104147
if (any(parameters %nin% c("tp", resourcecodedata::rscd_variables$name))) {
105-
errors <- parameters[parameters %nin% c("tp", resourcecodedata::rscd_variables$name)]
148+
errors <- parameters[
149+
parameters %nin% c("tp", resourcecodedata::rscd_variables$name)
150+
]
106151
stop(
107152
"Requested parameters do not exists in the database: ",
108-
paste0(errors, collapse = ", "), "."
153+
paste0(errors, collapse = ", "),
154+
"."
109155
)
110156
}
111157

@@ -144,7 +190,8 @@ get_parameters <- function(
144190
stop(
145191
"'start' is outside the covered period: ",
146192
paste(
147-
format(c(rscd_casandra_start_date, rscd_casandra_end_date),
193+
format(
194+
c(rscd_casandra_start_date, rscd_casandra_end_date),
148195
format = "%Y-%m-%d %H:%M %Z"
149196
),
150197
collapse = " \u2014 "
@@ -155,7 +202,8 @@ get_parameters <- function(
155202
stop(
156203
"'end' is outside the covered period: ",
157204
paste(
158-
format(c(rscd_casandra_start_date, rscd_casandra_end_date),
205+
format(
206+
c(rscd_casandra_start_date, rscd_casandra_end_date),
159207
format = "%Y-%m-%d %H:%M %Z"
160208
),
161209
collapse = " \u2014 "
@@ -173,14 +221,27 @@ get_parameters <- function(
173221
end = end
174222
)
175223

224+
# If first parameter retrieval failed, return NULL
225+
if (is.null(out)) {
226+
message("Failed to retrieve parameter: ", parameters[1])
227+
return(NULL)
228+
}
229+
176230
for (i in seq_len(length(parameters) - 1)) {
177231
temp <- get_parameters_raw(
178232
parameters[i + 1],
179233
node = node,
180234
start = start,
181235
end = end
182236
)
183-
out <- cbind(out, temp[, 2])
237+
238+
# If any subsequent parameter retrieval fails, return NULL
239+
if (is.null(temp)) {
240+
message("Failed to retrieve parameter: ", parameters[i + 1])
241+
return(NULL)
242+
}
243+
244+
out <- cbind.data.frame(out, temp[, 2])
184245
}
185246
out
186247
}

tests/testthat/Rplots.pdf

0 Bytes
Binary file not shown.

tests/testthat/_vcr/character_dates.yml

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_vcr/get_multiple_parameters.yml

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_vcr/get_single_parameter.yml

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_vcr/numeric_dates.yml

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)