diff --git a/R/get_all_data.R b/R/get_all_data.R index bb66baf..4d2ab88 100644 --- a/R/get_all_data.R +++ b/R/get_all_data.R @@ -45,11 +45,14 @@ get_all_data <- function( download_directory, glue::glue("{ds_name}.zip") ) - - httr2::request(zip_url) |> + resp <- httr2::request(zip_url) |> httr2::req_user_agent(getOption("rmelodi.req_user_agent")) |> + httr2::req_retry(max_tries = 3, max_seconds = 30, retry_on_failure = TRUE) |> httr2::req_progress() |> - httr2::req_perform(downloaded_zip_path) + httr2::req_perform(downloaded_zip_path) |> + tryCatch(error = \(e) message("Could not get data: ", conditionMessage(e))) + # API failed gracefully: exit quietly (return() only works inside a function) + if (is.null(resp)) return(invisible(NULL)) files_in_zip <- zip::zip_list(downloaded_zip_path) diff --git a/R/get_catalog.R b/R/get_catalog.R index 3d747b6..8f029d7 100644 --- a/R/get_catalog.R +++ b/R/get_catalog.R @@ -18,10 +18,15 @@ get_catalog <- function( url <- glue::glue("{getOption('rmelodi.base_url_api')}/catalog/all") message("Request all catalog : ", url) + all_dataset <- httr2::request(url) |> httr2::req_user_agent(getOption("rmelodi.req_user_agent")) |> + httr2::req_retry(max_tries = 3, max_seconds = 30, retry_on_failure = TRUE) |> httr2::req_perform() |> - httr2::resp_body_json(simplifyVector = TRUE) + httr2::resp_body_json(simplifyVector = TRUE) |> + tryCatch(error = \(e) message("Could not get data: ", conditionMessage(e))) + # API failed gracefully: exit quietly (return() only works inside a function) + if (is.null(all_dataset)) return(invisible(NULL)) # extract only usefull informations all_dataset_extract <- all_dataset |> diff --git a/R/get_data.R b/R/get_data.R index 1f01aa9..d4de711 100644 --- a/R/get_data.R +++ b/R/get_data.R @@ -18,8 +18,10 @@ get_data <- function( ) { max_result_api <- getOption("rmelodi.max_result_api") - # 1 - Count numer of lines of request + # 1 - Count number of lines of request request_count <- httr2::request(url) |> + httr2::req_user_agent(getOption("rmelodi.req_user_agent")) |> + httr2::req_retry(max_tries = 3, max_seconds = 30, retry_on_failure = TRUE) |> # Add Melodi /data parameters to only count lines httr2::req_url_query(totalCount = TRUE) |> httr2::req_url_query(maxResult = 0) @@ -27,9 +29,11 @@ get_data <- function( message("Total count request : ", request_count$url) data_count <- request_count |> - httr2::req_user_agent(getOption("rmelodi.req_user_agent")) |> httr2::req_perform() |> - httr2::resp_body_json(simplifyVector = TRUE) + httr2::resp_body_json(simplifyVector = TRUE) |> + tryCatch(error = \(e) message("Could not get data: ", conditionMessage(e))) + # API failed gracefully: exit quietly (return() only works inside a function) + if (is.null(data_count)) return(invisible(NULL)) count <- data_count[["paging"]][["count"]] message( @@ -49,15 +53,19 @@ get_data <- function( # 2 - request is OK, get results request <- httr2::request(url) |> + httr2::req_user_agent(getOption("rmelodi.req_user_agent")) |> + httr2::req_retry(max_tries = 3, max_seconds = 30, retry_on_failure = TRUE) |> # TODO useless ? this is the default value now httr2::req_url_query(idTerritoire = TRUE) |> # maximum maxResult authorized by Melodi API httr2::req_url_query(maxResult = max_result_api) result <- request |> - httr2::req_user_agent(getOption("rmelodi.req_user_agent")) |> httr2::req_perform() |> - httr2::resp_body_json(simplifyVector = TRUE) + httr2::resp_body_json(simplifyVector = TRUE) |> + tryCatch(error = \(e) message("Could not get data: ", conditionMessage(e))) + # API failed: exit quietly (return() only works inside a function) + if (is.null(result)) return(invisible(NULL)) # bind datas in one dataframe dimensions_obj <- result[["observations"]][["dimensions"]] diff --git a/R/get_file.R b/R/get_file.R index 2f62394..d712c74 100644 --- a/R/get_file.R +++ b/R/get_file.R @@ -30,10 +30,14 @@ get_file <- function( message("Request file : ", url) message("Downloaded path : ", path) - httr2::request(url) |> + resp <- httr2::request(url) |> httr2::req_user_agent(getOption("rmelodi.req_user_agent")) |> + httr2::req_retry(max_tries = 3, max_seconds = 30, retry_on_failure = TRUE) |> httr2::req_progress() |> - httr2::req_perform(path = path) + httr2::req_perform(path = path) |> + tryCatch(error = \(e) message("Could not get data: ", conditionMessage(e))) + # API failed gracefully: exit quietly (return() only works inside a function) + if (is.null(resp)) return(invisible(NULL)) return(path) } diff --git a/R/get_metadata.R b/R/get_metadata.R index 1189e3b..c92dd3e 100644 --- a/R/get_metadata.R +++ b/R/get_metadata.R @@ -16,8 +16,12 @@ get_metadata <- function( dataset <- httr2::request(url) |> httr2::req_user_agent(getOption("rmelodi.req_user_agent")) |> + httr2::req_retry(max_tries = 3, max_seconds = 30, retry_on_failure = TRUE) |> httr2::req_perform() |> - httr2::resp_body_json(simplifyVector = TRUE) + httr2::resp_body_json(simplifyVector = TRUE) |> + tryCatch(error = \(e) message("Could not get data: ", conditionMessage(e))) + # API failed gracefully: exit quietly (return() only works inside a function) + if (is.null(dataset)) return(invisible(NULL)) return(dataset) } diff --git a/R/get_range.R b/R/get_range.R index 697cdae..47e312c 100644 --- a/R/get_range.R +++ b/R/get_range.R @@ -40,8 +40,12 @@ get_range <- function( dataset <- httr2::request(url) |> httr2::req_user_agent(getOption("rmelodi.req_user_agent")) |> + httr2::req_retry(max_tries = 3, max_seconds = 30, retry_on_failure = TRUE) |> httr2::req_perform() |> - httr2::resp_body_json(simplifyVector = FALSE) + httr2::resp_body_json(simplifyVector = FALSE) |> + tryCatch(error = \(e) message("Could not get data: ", conditionMessage(e))) + # API failed gracefully: exit quietly (return() only works inside a function) + if (is.null(dataset)) return(invisible(NULL)) range <- dataset[["range"]] diff --git a/R/get_range_geo.R b/R/get_range_geo.R index 27bf525..1b1c8e0 100644 --- a/R/get_range_geo.R +++ b/R/get_range_geo.R @@ -29,8 +29,12 @@ get_range_geo <- function( dataset <- httr2::request(url) |> httr2::req_user_agent(getOption("rmelodi.req_user_agent")) |> + httr2::req_retry(max_tries = 3, max_seconds = 30, retry_on_failure = TRUE) |> httr2::req_perform() |> - httr2::resp_body_json(simplifyVector = FALSE) + httr2::resp_body_json(simplifyVector = FALSE) |> + tryCatch(error = \(e) message("Could not get data: ", conditionMessage(e))) + # API failed gracefully: exit quietly (return() only works inside a function) + if (is.null(dataset)) return(invisible(NULL)) # Keep GEO only range_geo <- dataset$range |> diff --git a/cran-comments.md b/cran-comments.md index cce0a4a..25244aa 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,3 +1,15 @@ +# Submission of {melodi} 1.2.0 + +This version fixes the issue "Packages that use Internet resources should fail gracefully" +by adding try/catch blocks and error messages to every function that calls Internet resources. + +## R CMD check results + +* checked on windows, mac, ubuntu (with GitHub actions) checked on CRAN [win-builder](https://win-builder.r-project.org/) + +0 errors | 0 warnings | 0 note + + # Submission of {melodi} 1.1.2 ## R CMD check results diff --git a/tests/testthat/test-get_catalog.R b/tests/testthat/test-get_catalog.R index 4d99935..e47943b 100644 --- a/tests/testthat/test-get_catalog.R +++ b/tests/testthat/test-get_catalog.R @@ -1,6 +1,5 @@ vcr::use_cassette("get_catalog_fr", { test_that("get catalog OK", { - expect_no_error( list_ds <- get_catalog() ) @@ -14,7 +13,6 @@ vcr::use_cassette("get_catalog_fr", { vcr::use_cassette("get_catalog_en", { test_that("get catalog lang EN OK", { - expect_no_error( list_ds_en <- get_catalog(lang = "en") ) @@ -28,10 +26,24 @@ vcr::use_cassette("get_catalog_en", { vcr::use_cassette("get_catalog_empty", { test_that("get catalog lang KO", { - expect_error( object = get_catalog(lang = "nexiste-pas"), regexp = "lang must be" ) }) }) + +# test valable pour toutes les fonctions requetant l'API Melodi +# en cas d'erreur côté API, verifier l'absence d'erreur du package (gestion "gracieuse") +test_that("Error API, OK", { + # erreur API simulee par erreur d'url + options(rmelodi.base_url_api = "https://url-qui-n-existe-pas.com") + + expect_message( + object = get_catalog(), + regexp = "Could not get data" + ) + + # on remet l'option par défaut pour que les autres tests passent + options(rmelodi.base_url_api = "https://api.insee.fr/melodi") +}) diff --git a/tests/testthat/test-get_data.R b/tests/testthat/test-get_data.R index d11fdce..c21a70c 100644 --- a/tests/testthat/test-get_data.R +++ b/tests/testthat/test-get_data.R @@ -52,11 +52,16 @@ vcr::use_cassette("get_data_popref_over", { vcr::use_cassette("get_data_not_exist", { test_that("get data DS not exists", { - expect_error( - object = get_data( - "https://api.insee.fr/melodi/data/DS_NEXISTEPAS" - ) + # expect_error( + # object = get_data( + # "https://api.insee.fr/melodi/data/DS_NEXISTEPAS" + # ) + # ) + expect_message( + res <- get_data("https://api.insee.fr/melodi/data/DS_NEXISTEPAS"), + "Could not get data" ) + expect_null(res) }) })