diff --git a/.Rhistory b/.Rhistory deleted file mode 100644 index 6e55fed..0000000 --- a/.Rhistory +++ /dev/null @@ -1,498 +0,0 @@ -hello <- function() { -print("Hello, world!") -} -hello() -View(hello) -get_all_countries() -get_all_countries() -df = get_all_countries() -df = get_all_countries() -View(df) -operations = get_all_operations() -library(dtmapi) -operations = get_all_operations() -operations = get_all_operations() -devtools::build() -devtools::check() -operations = get_all_operations() -View(operations) -devtools::load_all(".") -devtools::load_all(".") -devtools::load_all(".") -check() -library(devtools) -check() -devtools::load_all(".") ---- -title: "dtmapi document" -knitr::opts_chunk$set( -collapse = TRUE, -comment = "#>" -) -knitr::opts_chunk$set( -collapse = TRUE, -comment = "#>" -) -plot(1:10) -plot(10:1) -knitr::kable(head(mtcars, 10)) -knitr::opts_chunk$set( -collapse = TRUE, -comment = "#>" -) -plot(1:10) -plot(10:1) -knitr::kable(head(mtcars, 10)) -devtools::check() -devtools::build() -data = get_idp_admin0_data(CountryName = "Ethiopia") -View(data) -data = get_idp_admin0_data() -devtools::check() -devtools::build() -lt = get_idp_admin1_data() -lt = get_idp_admin1_data(Admin0Pcode = "ETH") -View(lt) -View(lt) -devtools::check() -devtools::build() -df = get_idp_admin2_data(Admin0Pcode = "ETH") -View(df) -df = get_idp_admin2_data(Admin0Pcode = "ETH", FromRoundNumber = "2", ToRoundNumber = "2") -nn = get_all_countries() -View(nn) -kj = get_all_operations() -View(kj) -library(roxygen2) -roxygen2::roxygenise() -devtools::document() -?get_all_countries -?get_all_operations -?get_all_countries -?get_idp_admin0_data -?get_idp_admin1_data -?get_idp_admin2_data -devtools::check() -devtools::build() -get_all_countries() -install.packages("config") -devtools::check() -devtools::check() -devtools::check() -devtools::check() -devtools::check() -library(dtmapi) -get_all_countries() -devtools::load_all(".") -get_all_countries() -library(dtmapi) -get_all_countries() -get_all_countries() -get_all_countries() -exit -clear -get_all_countries() -devtools::load_all(".") -get_all_countries() -devtools::load_all(".") -get_all_countries() -devtools::load_all(".") -get_all_countries() -devtools::check() -get_all_countries() -getdy = get_idp_admin0_data() -getdy = get_idp_admin0_data(CountryName = "Sudan") -View(getdy) -devtools::check() -devtools::check() -getdy = get_idp_admin0_data(CountryName = "Sudan") -View(getdy) -getdy = get_idp_admin0_data(CountryName = "Sudan") -devtools::check() -getdy = get_idp_admin0_data(CountryName = "Sudan") -View(getdy) -t = get_all_countries() -View(t) -?get_all_countries -?get_idp_admin0_data -?get_idp_admin0_data -?get_idp_admin0_data -devtools::document() -?get_idp_admin0_data -devtools::document() -?get_idp_admin0_data -?get_idp_admin0_data -?get_idp_admin0_data -devtools::document() -?get_idp_admin0_data -devtools::document() -?get_idp_admin1_data -devtools::load_all(".") -devtools::check() -devtools::build() -R CMD check --as-cran . -check --as-cran . -CMD check --as-cran . -R -devtools::check() -devtools::build() -devtools::check() -devtools::build() -g = get_idp_admin2_data() -g = get_idp_admin2_data(CountryName = "Ethiopia") -g -devtools::check() -devtools::check() -devtools::use_vignette("introduction") -install.packages("usethis") -library(usethis) -devtools::use_vignette("introduction") -library(usethis) -library(usethis) -usethis::use_vignette("introduction") -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::check() -devtools::check() -devtools::check() -devtools::check() -devtools::check() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::check() -devtools::check() -get_all_countries() -get_all_operations() -get_idp_admin0_data(Admin0Pcode = "AFG") -get_idp_admin1_data(Admin0Pcode = "ETH") -get_idp_admin2_data(Admin0Pcode = "ETH") -install.packages("testthat") -library(testthat) -test_dir("tests/testthat") -devtools::test() -devtools::document() -devtools::document() -devtools::document() -? get_all_operations -?get_idp_admin0_data -?get_idp_admin0_data -devtools::document() -?get_idp_admin2_data -devtools::build_vignettes() -devtools::build_vignettes() -?get_idp_admin0_data -devtools::build_vignettes() -devtools::build_vignettes() -devtools::check() -devtools::check() -devtools::check() -devtools::check() -devtools::check() -devtools::check() -devtools::document() -?get_all_countries -abc = get_all_countries() -View(abc) -devtools::build() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -library(dtmapi) -install.packages("devtools") -devtools::install_github("Displacement-Tracking-Matrix/dtmapi-R") -devtools::install_github("Displacement-Tracking-Matrix/dtmapi-R") -devtools::install_github("Displacement-Tracking-Matrix/dtmapi-R") -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -install.packages("tinytex") -tinytex::install_tinytex() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::check() -devtools::build() -devtools::check() -# Install released version from CRAN -install.packages("pkgdown") -install.packages("pkgdown") -getwd() -install.packages("pkgdown") -install.packages("devtools") -library(dtmapi) -get_all_countries() -install.packages("devtools") -install.packages("pkgdown") -git push --force -devtools::build_vignettes() -devtools::check() -pkgdown::build_site() -devtools::check() -devtools::build_vignettes() -pkgdown::build_site() -devtools::build() -pkgdown::build_site() -devtools::check() -devtools::build() -devtools::check() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::check() -devtools::check() -devtools::build_vignettes() -devtools::check() -devtools::build_vignettes() -pkgdown::build_site() -pkgdown::build_site() -devtools::check() -devtools::build_vignettes() -pkgdown::build_site() -devtools::check() -pkgdown::build_site() -devtools::check() -devtools::build_vignettes() -pkgdown::build_site() -devtools::check() -devtools::check() -devtools::build_vignettes() -pkgdown::build_site() -pkgdown::build_site() -pkgdown::build_site() -library(dtmapi) -install.packages("dtmapi") -pkgdown::build_site() -pkgdown::build_site() -pkgdown::build_site() -pkgdown::build_site() -pkgdown::build_site() -devtools::build_vignettes() -devtools::check() -library(dtmapi) -abc <- get_idp_admin0_data(Operation = "abc") -#' @param ToRoundNumber Optional; Ending round number for the data collection range. -#' @return A data frame containing the IDP Admin0 data matching the specified criteria. -#' @export -#' @examples -#' # Fetch IDP data at Admin Level 0 -#' idp_admin0_df <- get_idp_admin0_data(CountryName='Ethiopia', FromRoundNumber=1, ToRoundNumber=10) -#' head(idp_admin0_df) -#' @importFrom httr GET status_code content -#' @importFrom jsonlite fromJSON -#' @importFrom config get -get_idp_admin0_data <- function( -Operation = NULL, -CountryName = NULL, -Admin0Pcode = NULL, -FromReportingDate = NULL, -ToReportingDate = NULL, -FromRoundNumber = 0, -ToRoundNumber = 0 -) { -# Retrieve the API URL from the configuration file -# Load configuration -api_url <- "https://dtmapi.iom.int/api/idpAdmin0Data/GetAdmin0Datav2" -# Set up query parameters -params <- list( -Operation = Operation, -CountryName = CountryName, -Admin0Pcode = Admin0Pcode, -FromReportingDate = FromReportingDate, -ToReportingDate = ToReportingDate, -FromRoundNumber = FromRoundNumber, -ToRoundNumber = ToRoundNumber -) -tryCatch({ -# Send GET request to the API with parameters -response <- GET(api_url, query = params) -# Check if the request was successful -if (status_code(response) != 200) { -stop("Failed to fetch data. Status code: ", status_code(response)) -} -# Parse the JSON content -data <- content(response, "text", encoding = "UTF-8") -json_data <- fromJSON(data, flatten = TRUE) -# Check if the request was successful and extract the result -if (json_data$isSuccess) { -# Return the result as a data frame -return(as.data.frame(json_data$result)) -} else { -# Handle API-specific errors -stop("API error: ", json_data$errorMessages[1]) -} -}, error = function(e) { -# Handle and report errors -stop("API request failed: ", e$message) -}) -} -@importFrom config get -#' -#' @return A data frame containing the list of all countries. -#' @export -#' @examples -#' # Fetch all countries -#' countries_df <- get_all_countries() -#' head(countries_df) -#' @importFrom httr GET status_code content -#' @importFrom jsonlite fromJSON -#' @importFrom config get -get_all_countries <- function() { -tryCatch({ -# Retrieve the API URL from the configuration file -api_url <- "https://dtmapi.iom.int/api/Common/GetAllCountryList" -# Send GET request to the API -response <- GET(api_url) -# Check if the request was successful -if (status_code(response) != 200) { -stop("Failed to fetch data. Status code: ", status_code(response)) -} -# Parse the JSON content and extract the result as a data frame -data <- content(response, "text") -df <- fromJSON(data, flatten = TRUE)$result -# Return the data frame -return(df) -}, error = function(e) { -# Handle and report errors -stop("API request failed: ", e$message) -}) -} -abc <- get_idp_admin0_data(Operation = "abc") -abc <- get_idp_admin0_data(CountryName = ) -abc <- get_idp_admin0_data(CountryName = "Ethiopia", FromRoundNumber = 7, ToRoundNumber = 5) -remove.packages("dtmapi") -library(dtmapi) -get_all_countries() -library(dtmapi) -get_all_countries() -remove.packages("dtmapi") -devtools::check() -devtools::check() -devtools::check() -install.packages("httr2") -devtools::check() -devtools::build_vignettes() -devtools::build() -install.packages("magrittr") -install.packages("dplyr") -devtools::build() -install.packages("magrittr") -install.packages("dplyr") -devtools::build() -devtools::build() -install.packages("magrittr") -install.packages("dplyr") -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -install.packages("httr2") -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build() -devtools::install("/Users/bangtranl/Work/dtmapi-r/dtmapi_0.0.1.tar.gz") -library(dtmapi) -install.packages("/Users/bangtranl/Work/dtmapi-r/dtmapi_0.0.1.tar.gz", repos = NULL, type = "source") -library(dtmapi) -abc = get_all_countries() -install.packages("/Users/bangtranl/Work/dtmapi-r/dtmapi_0.0.1.tar.gz") -library(dtmapi) -abc = get_all_countries() -devtools::check() -devtools::check() -devtools::check() -devtools::check() -devtools::check() -devtools::check() -devtools::check() -devtools::check() -devtools::build() -install.packages("/Users/bangtranl/Work/dtmapi-r/dtmapi_0.0.1.tar.gz") -library(dtmapi) -abc = get_all_countries() -abc -a = get_idp_admin0_data(CountryName = "Ehiopia") -a = get_idp_admin0_data(CountryName = "Ethiopia") -a -a = get_idp_admin1_data(CountryName = "Ethiopia") -View(a) -a = get_idp_admin2_data(CountryName = "Ethiopia", FromRoundNumber = 1, ToRoundNumber = 2) -pkgdown::build_site() -pkgdown::build_site() -devtools::build_vignettes() -pkgdown::build_site() -devtools::check() -devtools::check() -devtools::build() -pkgdown::build_site() -devtools::build_vignettes() -pkgdown::build_site() -git status -devtools::build() -devtools::build() -devtools::check() -devtools::check() -devtools::build() -devtools::check() -devtools::build() -devtools::check() -devtools::document() -devtools::build() -devtools::check() -devtools::document() -devtools::document() -devtools::build_vignettes() -devtools::build() -devtools::check() -devtools::document() -devtools::build_vignettes() -devtools::build_vignettes() -devtools::build() -devtools::check() -unlink("doc", recursive = TRUE) # Delete old doc/ folder -unlink("vignettes/*.html") # Remove old vignette HTML files -unlink("vignettes/*.R") # Remove old vignette R scripts -devtools::build_vignettes() -vignette(package = "dtmapi") -devtools::document() -devtools::build_vignettes() -devtools::check() -devtools::document() -devtools::build_vignettes() -devtools::build() -devtools::check() -devtools::check() -pkgdown::build_site() -devtools::build() -devtools::release() -devtools::release() -check_rhub() -devtools::check_rhub() -devtools::check_rhub() -devtools::check_rhubv2() -devtools::check() -devtools::release() -use_cran_comments() -usethis::use_cran_comments() -usethis::use_cran_comments() -devtools::submit_cran() -git status diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml new file mode 100644 index 0000000..fdb499f --- /dev/null +++ b/.github/workflows/R-CMD-check.yaml @@ -0,0 +1,52 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + pull_request: + +name: R-CMD-check.yaml + +permissions: read-all + +jobs: + R-CMD-check: + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + + strategy: + fail-fast: false + matrix: + config: + - {os: macos-latest, r: 'release'} + - {os: windows-latest, r: 'release'} + - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} + - {os: ubuntu-latest, r: 'release'} + - {os: ubuntu-latest, r: 'oldrel-1'} + + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + DTMAPIR_KEY: ${{ secrets.DTMAPIR_KEY}} + + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::rcmdcheck + needs: check + + - uses: r-lib/actions/check-r-package@v2 + with: + upload-snapshots: true + build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml new file mode 100644 index 0000000..3116430 --- /dev/null +++ b/.github/workflows/test-coverage.yaml @@ -0,0 +1,61 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + pull_request: + +name: test-coverage.yaml + +permissions: read-all + +jobs: + test-coverage: + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::covr, any::xml2 + needs: coverage + + - name: Test coverage + run: | + cov <- covr::package_coverage( + quiet = FALSE, + clean = FALSE, + install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") + ) + print(cov) + covr::to_cobertura(cov) + shell: Rscript {0} + + - uses: codecov/codecov-action@v5 + with: + # Fail if error if not on PR, or if on PR and token is given + fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }} + files: ./cobertura.xml + plugins: noop + disable_search: true + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Show testthat output + if: always() + run: | + ## -------------------------------------------------------------------- + find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true + shell: bash + + - name: Upload test results + if: failure() + uses: actions/upload-artifact@v4 + with: + name: coverage-test-failures + path: ${{ runner.temp }}/package diff --git a/.gitignore b/.gitignore index c48f0ba..f234b92 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ inst/doc /doc/ /Meta/ +.Renviron +.Rhistory \ No newline at end of file diff --git a/DESCRIPTION b/DESCRIPTION index eb1d067..85bba09 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,10 @@ Package: dtmapi Title: Fetching Data from the 'Displacement Tracking Matrix' Version: 0.0.3 -Authors@R: - person("Luong Bang", "Tran", email = "lutran@iom.int", role = c("aut", "cre")) +Authors@R: c( + person("Luong Bang", "Tran", email = "lutran@iom.int", role = c("aut", "cre")), + person("Assad", "Asil Companioni", email = "aasil@iom.int", role = c("aut")) + ) Description: Allows humanitarian community, academia, media, government, and non-governmental organizations to utilize the data collected by the 'Displacement Tracking Matrix' (), a unit in the International Organization for Migration. This also provides non-sensitive Internally Displaced Person figures, aggregated at the country, Admin 1 (states, provinces, or equivalent), and Admin 2 (smaller administrative areas) levels. URL: https://github.com/Displacement-Tracking-Matrix/dtmapi-R, https://displacement-tracking-matrix.github.io/dtmapi-R/ License: MIT + file LICENSE @@ -11,10 +13,9 @@ Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.2 Imports: httr2, - jsonlite, - magrittr + testthat (>= 3.1.0), + askpass Suggests: knitr, - rmarkdown, - testthat (>= 3.1.0) + rmarkdown VignetteBuilder: knitr diff --git a/NAMESPACE b/NAMESPACE index f70c760..2975198 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -5,10 +5,15 @@ export(get_all_operations) export(get_idp_admin0_data) export(get_idp_admin1_data) export(get_idp_admin2_data) +export(get_subscription_key) +export(set_subscription_key) +importFrom(askpass,askpass) +importFrom(httr2,req_headers_redacted) importFrom(httr2,req_perform) importFrom(httr2,req_url_query) importFrom(httr2,request) +importFrom(httr2,resp_body_json) importFrom(httr2,resp_body_string) importFrom(httr2,resp_status) -importFrom(jsonlite,fromJSON) -importFrom(magrittr,"%>%") +importFrom(httr2,secret_decrypt) +importFrom(testthat,is_testing) diff --git a/R/get_all_countries.R b/R/get_all_countries.R index b5167b9..a1338d6 100644 --- a/R/get_all_countries.R +++ b/R/get_all_countries.R @@ -1,7 +1,3 @@ -library(httr2) -library(jsonlite) -library(magrittr) # For the `%>%` operator - #' Fetch All Countries #' #' Retrieve all countries for which DTM data is publicly available through the API. @@ -9,31 +5,32 @@ library(magrittr) # For the `%>%` operator #' @return A data frame containing the list of all countries. #' @export #' @examples -#' # Fetch all countries +#' \dontrun{ #' countries_df <- get_all_countries() #' head(countries_df) -#' @importFrom httr2 request req_perform resp_status resp_body_string -#' @importFrom magrittr %>% -#' @importFrom jsonlite fromJSON +#' } +#' @importFrom httr2 request req_perform resp_status resp_body_json req_headers_redacted + get_all_countries <- function() { tryCatch({ - # Retrieve the API URL - api_url <- "https://dtmapi.iom.int/api/Common/GetAllCountryList" + api_url <- "https://dtm-apim.iom.int/v3/CountryList" - # Send GET request to the API using httr2 - response <- request(api_url) %>% req_perform() + response <- + request(api_url) |> + req_headers_redacted("Cache-Control" = "no-cache", + "Ocp-Apim-Subscription-Key" = get_subscription_key() + ) |> + req_perform() # Check if the request was successful if (resp_status(response) != 200) { stop("Failed to fetch data. Status code: ", resp_status(response)) } - # Parse the JSON content and extract the result as a data frame - data <- resp_body_string(response) - df <- fromJSON(data, flatten = TRUE)$result + json_data <- resp_body_json(response, simplifyVector = TRUE) + df <- as.data.frame(json_data$result) # as.data.frame() for consistency's sake. - # Return the data frame return(df) }, error = function(e) { diff --git a/R/get_all_operations.R b/R/get_all_operations.R index 4b64dde..9db8310 100644 --- a/R/get_all_operations.R +++ b/R/get_all_operations.R @@ -1,7 +1,3 @@ -library(httr2) -library(jsonlite) -library(magrittr) # For the `%>%` operator - #' Fetch All Operations #' #' Retrieve all operations for which DTM data is publicly available through the API. @@ -9,20 +5,24 @@ library(magrittr) # For the `%>%` operator #' @return A data frame containing the list of all operations. #' @export #' @examples +#' \dontrun{ #' # Fetch all operations #' operations_df <- get_all_operations() #' head(operations_df) -#' @importFrom httr2 request req_perform resp_status resp_body_string -#' @importFrom magrittr %>% -#' @importFrom jsonlite fromJSON +#' } +#' @importFrom httr2 request req_perform resp_status resp_body_json req_headers_redacted + get_all_operations <- function() { tryCatch({ - # Load configuration - api_url <- "https://dtmapi.iom.int/api/Common/GetAllOperationList" + api_url <- "https://dtm-apim.iom.int/v3/OperationList" - # Send GET request to the API using httr2 - response <- request(api_url) %>% req_perform() + response <- + request(api_url) |> + req_headers_redacted("Cache-Control" = "no-cache", + "Ocp-Apim-Subscription-Key" = get_subscription_key() + ) |> + req_perform() # Check if the request was successful if (resp_status(response) != 200) { @@ -30,8 +30,8 @@ get_all_operations <- function() { } # Parse the JSON content and extract the result as a data frame - data <- resp_body_string(response) - df <- fromJSON(data, flatten = TRUE)$result + json_data <- resp_body_json(response, simplifyVector = TRUE) + df <- as.data.frame(json_data$result) # as.data.frame() for consistency's sake. # Return the data frame return(df) diff --git a/R/get_idp_admin0_data.R b/R/get_idp_admin0_data.R index 777fa5a..2f9cb5f 100644 --- a/R/get_idp_admin0_data.R +++ b/R/get_idp_admin0_data.R @@ -1,7 +1,3 @@ -library(httr2) -library(jsonlite) -library(magrittr) # For the `%>%` operator - #' Fetch IDP Admin0 Data #' #' Retrieve IDP data at Admin 0 level based on specified parameters. @@ -17,12 +13,15 @@ library(magrittr) # For the `%>%` operator #' @return A data frame containing the IDP Admin0 data matching the specified criteria. #' @export #' @examples +#' \dontrun{ #' # Fetch IDP data at Admin Level 0 -#' idp_admin0_df <- get_idp_admin0_data(CountryName='Ethiopia', FromRoundNumber=1, ToRoundNumber=10) +#' idp_admin0_df <- get_idp_admin0_data(CountryName = "Ethiopia", +#' FromRoundNumber = 1, +#' ToRoundNumber = 10) #' head(idp_admin0_df) -#' @importFrom httr2 request req_perform req_url_query resp_status resp_body_string -#' @importFrom magrittr %>% -#' @importFrom jsonlite fromJSON +#' } +#' @importFrom httr2 request req_perform req_url_query resp_status resp_body_json req_headers_redacted + get_idp_admin0_data <- function( Operation = NULL, CountryName = NULL, @@ -32,11 +31,9 @@ get_idp_admin0_data <- function( FromRoundNumber = 0, ToRoundNumber = 0 ) { - # Retrieve the API URL - api_url <- "https://dtmapi.iom.int/api/idpAdmin0Data/GetAdmin0Datav2" + api_url <- "https://dtm-apim.iom.int/v3/IdpAdmin0Data" - # Set up query parameters - params <- list( + query_params <- list( Operation = Operation, CountryName = CountryName, Admin0Pcode = Admin0Pcode, @@ -47,23 +44,22 @@ get_idp_admin0_data <- function( ) tryCatch({ - # Send GET request to the API with parameters using httr2 - response <- request(api_url) %>% - req_url_query(!!!params) %>% + response <- + request(api_url) |> + req_headers_redacted("Cache-Control" = "no-cache", + "Ocp-Apim-Subscription-Key" = get_subscription_key() + ) |> + req_url_query(!!!query_params) |> req_perform() - # Check if the request was successful if (resp_status(response) != 200) { stop("Failed to fetch data. Status code: ", resp_status(response)) } - # Parse the JSON content - data <- resp_body_string(response, encoding = "UTF-8") - json_data <- fromJSON(data, flatten = TRUE) + # Retrieve content as parsed JSON: simplifyVector helps to later return a dataframe. + json_data <- resp_body_json(response, simplifyVector = TRUE) - # Check if the request was successful and extract the result if (json_data$isSuccess) { - # Return the result as a data frame return(as.data.frame(json_data$result)) } else { # Handle API-specific errors diff --git a/R/get_idp_admin1_data.R b/R/get_idp_admin1_data.R index 35e5f08..fd959ab 100644 --- a/R/get_idp_admin1_data.R +++ b/R/get_idp_admin1_data.R @@ -1,7 +1,3 @@ -library(httr2) -library(jsonlite) -library(magrittr) # For the `%>%` operator - #' Fetch IDP Admin1 Data #' #' Retrieve IDP data at Admin 1 level based on specified parameters. @@ -19,12 +15,13 @@ library(magrittr) # For the `%>%` operator #' @return A data frame containing the IDP Admin1 data matching the specified criteria. #' @export #' @examples +#' \dontrun{ #' # Fetch IDP data at Admin Level 1 -#' idp_admin1_df <- get_idp_admin1_data(CountryName='Sudan', Admin1Name="Blue Nile") +#' idp_admin1_df <- get_idp_admin1_data(CountryName = "Sudan", Admin1Name = "Blue Nile") #' head(idp_admin1_df) -#' @importFrom httr2 request req_perform req_url_query resp_status resp_body_string -#' @importFrom magrittr %>% -#' @importFrom jsonlite fromJSON +#' } +#' @importFrom httr2 request req_perform req_url_query resp_status resp_body_json req_headers_redacted + get_idp_admin1_data <- function( Operation = NULL, CountryName = NULL, @@ -36,11 +33,9 @@ get_idp_admin1_data <- function( FromRoundNumber = 0, ToRoundNumber = 0 ) { - # Retrieve the API URL - api_url <- "https://dtmapi.iom.int/api/idpAdmin1Data/GetAdmin1Datav2" + api_url <- "https://dtm-apim.iom.int/v3/IdpAdmin1Data" - # Set up query parameters - params <- list( + query_params <- list( Operation = Operation, CountryName = CountryName, Admin0Pcode = Admin0Pcode, @@ -53,23 +48,22 @@ get_idp_admin1_data <- function( ) tryCatch({ - # Send GET request to the API with parameters using httr2 - response <- request(api_url) %>% - req_url_query(!!!params) %>% + response <- + request(api_url) |> + req_headers_redacted("Cache-Control" = "no-cache", + "Ocp-Apim-Subscription-Key" = get_subscription_key() + ) |> + req_url_query(!!!query_params) |> req_perform() - # Check if the request was successful if (resp_status(response) != 200) { stop("Failed to fetch data. Status code: ", resp_status(response)) } - # Parse the JSON content - data <- resp_body_string(response, encoding = "UTF-8") - json_data <- fromJSON(data, flatten = TRUE) + # Retrieve content as parsed JSON: simplifyVector helps to later return a dataframe. + json_data <- resp_body_json(response, simplifyVector = TRUE) - # Check if the request was successful and extract the result if (json_data$isSuccess) { - # Return the result as a data frame return(as.data.frame(json_data$result)) } else { # Handle API-specific errors diff --git a/R/get_idp_admin2_data.R b/R/get_idp_admin2_data.R index b2749ff..9c653fa 100644 --- a/R/get_idp_admin2_data.R +++ b/R/get_idp_admin2_data.R @@ -1,7 +1,3 @@ -library(httr2) -library(jsonlite) -library(magrittr) # For the `%>%` operator - #' Fetch IDP Admin2 Data #' #' Retrieve IDP data at Admin 2 level based on specified parameters. @@ -23,12 +19,11 @@ library(magrittr) # For the `%>%` operator #' @examples #' \dontrun{ #' # Fetch IDP data at Admin Level 2 -#' idp_admin2_df <- get_idp_admin2_data(Operation='Yemen conflict', CountryName="Yemen") +#' idp_admin2_df <- get_idp_admin2_data(Operation = "Yemen conflict", CountryName = "Yemen") #' head(idp_admin2_df) #' } -#' @importFrom httr2 request req_perform req_url_query resp_status resp_body_string -#' @importFrom magrittr %>% -#' @importFrom jsonlite fromJSON +#' @importFrom httr2 request req_perform req_url_query resp_status resp_body_string req_headers_redacted + get_idp_admin2_data <- function( Operation = NULL, CountryName = NULL, @@ -42,11 +37,9 @@ get_idp_admin2_data <- function( FromRoundNumber = 0, ToRoundNumber = 0 ) { - # Retrieve the API URL - api_url <- "https://dtmapi.iom.int/api/idpAdmin2Data/GetAdmin2Datav2" + api_url <- "https://dtm-apim.iom.int/v3/IdpAdmin2Data" - # Set up query parameters - params <- list( + query_params <- list( Operation = Operation, CountryName = CountryName, Admin0Pcode = Admin0Pcode, @@ -61,9 +54,12 @@ get_idp_admin2_data <- function( ) tryCatch({ - # Send GET request to the API with parameters using httr2 - response <- request(api_url) %>% - req_url_query(!!!params) %>% + response <- + request(api_url) |> + req_headers_redacted("Cache-Control" = "no-cache", + "Ocp-Apim-Subscription-Key" = get_subscription_key() + ) |> + req_url_query(!!!query_params) |> req_perform() # Check if the request was successful @@ -71,13 +67,10 @@ get_idp_admin2_data <- function( stop("Failed to fetch data. Status code: ", resp_status(response)) } - # Parse the JSON content - data <- resp_body_string(response, encoding = "UTF-8") - json_data <- fromJSON(data, flatten = TRUE) + # Retrieve content as parsed JSON: simplifyVector helps to later return a dataframe. + json_data <- resp_body_json(response, simplifyVector = TRUE) - # Check if the request was successful and extract the result if (json_data$isSuccess) { - # Return the result as a data frame return(as.data.frame(json_data$result)) } else { # Handle API-specific errors diff --git a/R/get_subscription_key.R b/R/get_subscription_key.R new file mode 100644 index 0000000..833c178 --- /dev/null +++ b/R/get_subscription_key.R @@ -0,0 +1,44 @@ +#' Retrieval of an API subscription key from the environment. +#' +#' The DTM API subscription key is returned, provided that it is available in +#' the R session as an environment variable. Users will usually need to set +#' the DTM_SUBSCRIPTION_KEY environment variable through a .Renviron file or +#' by calling `set_subscription_key()`. +#' +#' On the other hand, if the TESTTHAT environment variable is true, indicating +#' that unit tests are being run by the package maintainers, then the +#' subscription key is returned through different means. +#' @return A string representing a given subscription key for the DTM API. +#' @export +#' @examples +#' \dontrun{ +#' # Generally, calling set_subscription_key() without the key as an argument is best, +#' # as the user can then be prompted to input the key without typing it directly +#' # into the console, making it more secure and less likely to exposed. +#' set_subscription_key() +#' } +#' @importFrom httr2 secret_decrypt +#' @importFrom testthat is_testing +get_subscription_key <- function() { + key <- Sys.getenv("DTM_SUBSCRIPTION_KEY") + if (!identical(key, "")) { + return(key) + } + + if (is_testing()) { + return(testing_key()) + } else { + stop( + paste("No API key found, please supply with set_subscription_key() or", + "otherwise specifying the DTM_SUBSCRIPTION_KEY environment variable.") + ) + } +} + +testing_key_encrypted <- "gs-XVH-qdoewh5zjCEMPXrrrKDHqs5L-3X43yAPEY0rqBcwEGa2p_mTo89Ki5HqZ" + +testing_key <- function() { + secret_decrypt(encrypted = testing_key_encrypted, + key = "DTMAPIR_KEY" # Environment variable name as a string + ) +} \ No newline at end of file diff --git a/R/set_subscription_key.R b/R/set_subscription_key.R new file mode 100644 index 0000000..a1fbde5 --- /dev/null +++ b/R/set_subscription_key.R @@ -0,0 +1,25 @@ +#' Set the user's API subscription key in order to make the API calls. +#' +#' The API will be stored as an environmental variable named "DTM_API_KEY". +#' @param key +#' Either NULL or a string representing the key. NULL is preferable: using it +#' will prompt the user to type the subscription key in a graphical user +#' interface that masks it. +#' @return Nothing. Creates / overwrites an environmental variable as a side effect. +#' @export +#' @examples +#' \dontrun{ +#' # Generally, calling set_subscription_key() without the key as an argument is best, +#' # as the user can then be prompted to input the key without typing it directly +#' # into the console, making it more secure and less likely to exposed. +#' set_subscription_key() +#' } +#' @importFrom askpass askpass + +set_subscription_key <- function(key = NULL) { + if (is.null(key)) { + Sys.setenv("DTM_SUBSCRIPTION_KEY" = askpass("Please enter your subscription key.")) + } else { + Sys.setenv("DTM_SUBSCRIPTION_KEY" = key) + } +} \ No newline at end of file diff --git a/README.md b/README.md index 0532063..69c710b 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ + +[![R-CMD-check](https://github.com/a-asil-companioni/dtmapi-R/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/a-asil-companioni/dtmapi-R/actions/workflows/R-CMD-check.yaml) +[![Codecov test coverage](https://codecov.io/gh/a-asil-companioni/dtmapi-R/graph/badge.svg)](https://app.codecov.io/gh/a-asil-companioni/dtmapi-R) + +

DTM Logo

- ----------------- ## About diff --git a/man/get_all_countries.Rd b/man/get_all_countries.Rd index e27924a..09ca06a 100644 --- a/man/get_all_countries.Rd +++ b/man/get_all_countries.Rd @@ -14,7 +14,6 @@ Retrieve all countries for which DTM data is publicly available through the API. } \examples{ \dontrun{ -# Fetch all countries countries_df <- get_all_countries() head(countries_df) } diff --git a/man/get_idp_admin0_data.Rd b/man/get_idp_admin0_data.Rd index 3175e43..8097b90 100644 --- a/man/get_idp_admin0_data.Rd +++ b/man/get_idp_admin0_data.Rd @@ -39,7 +39,9 @@ At least one of the following parameters must be provided: Operation, CountryNam \examples{ \dontrun{ # Fetch IDP data at Admin Level 0 -idp_admin0_df <- get_idp_admin0_data(CountryName='Ethiopia', FromRoundNumber=1, ToRoundNumber=10) +idp_admin0_df <- get_idp_admin0_data(CountryName = "Ethiopia", + FromRoundNumber = 1, + ToRoundNumber = 10) head(idp_admin0_df) } } diff --git a/man/get_idp_admin1_data.Rd b/man/get_idp_admin1_data.Rd index 2b962d5..6541cb1 100644 --- a/man/get_idp_admin1_data.Rd +++ b/man/get_idp_admin1_data.Rd @@ -45,7 +45,7 @@ At least one of the following parameters must be provided: Operation, CountryNam \examples{ \dontrun{ # Fetch IDP data at Admin Level 1 -idp_admin1_df <- get_idp_admin1_data(CountryName='Sudan', Admin1Name="Blue Nile") +idp_admin1_df <- get_idp_admin1_data(CountryName = "Sudan", Admin1Name = "Blue Nile") head(idp_admin1_df) } } diff --git a/man/get_idp_admin2_data.Rd b/man/get_idp_admin2_data.Rd index b1abedb..b2bc372 100644 --- a/man/get_idp_admin2_data.Rd +++ b/man/get_idp_admin2_data.Rd @@ -51,7 +51,7 @@ At least one of the following parameters must be provided: Operation, CountryNam \examples{ \dontrun{ # Fetch IDP data at Admin Level 2 -idp_admin2_df <- get_idp_admin2_data(Operation='Yemen conflict', CountryName="Yemen") +idp_admin2_df <- get_idp_admin2_data(Operation = "Yemen conflict", CountryName = "Yemen") head(idp_admin2_df) } } diff --git a/man/get_subscription_key.Rd b/man/get_subscription_key.Rd new file mode 100644 index 0000000..a68c2b3 --- /dev/null +++ b/man/get_subscription_key.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/get_subscription_key.R +\name{get_subscription_key} +\alias{get_subscription_key} +\title{Retrieval of an API subscription key from the environment.} +\usage{ +get_subscription_key() +} +\value{ +A string representing a given subscription key for the DTM API. +} +\description{ +The DTM API subscription key is returned, provided that it is available in +the R session as an environment variable. Users will usually need to set +the DTM_SUBSCRIPTION_KEY environment variable through a .Renviron file or +by calling \code{set_subscription_key()}. +} +\details{ +On the other hand, if the TESTTHAT environment variable is true, indicating +that unit tests are being run by the package maintainers, then the +subscription key is returned through different means. +} +\examples{ +\dontrun{ +# Generally, calling set_subscription_key() without the key as an argument is best, +# as the user can then be prompted to input the key without typing it directly +# into the console, making it more secure and less likely to exposed. +set_subscription_key() +} +} diff --git a/man/set_subscription_key.Rd b/man/set_subscription_key.Rd new file mode 100644 index 0000000..9bf1b8c --- /dev/null +++ b/man/set_subscription_key.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/set_subscription_key.R +\name{set_subscription_key} +\alias{set_subscription_key} +\title{Set the user's API subscription key in order to make the API calls.} +\usage{ +set_subscription_key(key = NULL) +} +\arguments{ +\item{key}{Either NULL or a string representing the key. NULL is preferable: using it +will prompt the user to type the subscription key in a graphical user +interface that masks it.} +} +\value{ +Nothing. Creates / overwrites an environmental variable as a side effect. +} +\description{ +The API will be stored as an environmental variable named "DTM_API_KEY". +} +\examples{ +\dontrun{ +# Generally, calling set_subscription_key() without the key as an argument is best, +# as the user can then be prompted to input the key without typing it directly +# into the console, making it more secure and less likely to exposed. +set_subscription_key() +} +} diff --git a/tests/testthat/test-get_all_countries.R b/tests/testthat/test-get_all_countries.R index 2c47321..0bfd464 100644 --- a/tests/testthat/test-get_all_countries.R +++ b/tests/testthat/test-get_all_countries.R @@ -1,6 +1,3 @@ -library(testthat) -library(dtmapi) - test_that("get_all_countries works", { skip_on_cran() # Skip test on CRAN diff --git a/tests/testthat/test-get_all_operations.R b/tests/testthat/test-get_all_operations.R index 222cbf2..660a6e2 100644 --- a/tests/testthat/test-get_all_operations.R +++ b/tests/testthat/test-get_all_operations.R @@ -1,6 +1,3 @@ -library(testthat) -library(dtmapi) - test_that("get_all_operations works", { skip_on_cran() # Skip test on CRAN diff --git a/tests/testthat/test-get_idp_admin0_data.R b/tests/testthat/test-get_idp_admin0_data.R index 0b46c83..ad214fc 100644 --- a/tests/testthat/test-get_idp_admin0_data.R +++ b/tests/testthat/test-get_idp_admin0_data.R @@ -1,10 +1,11 @@ -library(testthat) -library(dtmapi) - test_that("get_idp_admin0_data works", { - skip_on_cran() # Skip test on CRAN + skip_on_cran() + + idp_admin0_df <- + get_idp_admin0_data(CountryName = "Ethiopia", + FromRoundNumber = 1, + ToRoundNumber = 10) - idp_admin0_df <- get_idp_admin0_data(CountryName='Ethiopia', FromRoundNumber=1, ToRoundNumber=10) expect_s3_class(idp_admin0_df, "data.frame") expect_true(nrow(idp_admin0_df) > 0) }) diff --git a/tests/testthat/test-get_idp_admin1_data.R b/tests/testthat/test-get_idp_admin1_data.R index db4f615..0ccdb03 100644 --- a/tests/testthat/test-get_idp_admin1_data.R +++ b/tests/testthat/test-get_idp_admin1_data.R @@ -1,10 +1,12 @@ -library(testthat) -library(dtmapi) - test_that("get_idp_admin1_data works", { skip_on_cran() # Skip test on CRAN - idp_admin1_df <- get_idp_admin1_data(CountryName='Sudan', Admin1Name="Blue Nile", FromReportingDate='2020-01-01', ToReportingDate='2024-08-15') + idp_admin1_df <- + get_idp_admin1_data(CountryName = "Sudan", + Admin1Name = "Blue Nile", + FromReportingDate = "2020-01-01", + ToReportingDate = "2024-08-15") + expect_s3_class(idp_admin1_df, "data.frame") expect_true(nrow(idp_admin1_df) > 0) }) diff --git a/tests/testthat/test-get_idp_admin2_data.R b/tests/testthat/test-get_idp_admin2_data.R index 26d3f01..ba33c99 100644 --- a/tests/testthat/test-get_idp_admin2_data.R +++ b/tests/testthat/test-get_idp_admin2_data.R @@ -1,6 +1,3 @@ -library(testthat) -library(dtmapi) - test_that("get_idp_admin2_data works", { skip_on_cran() # Skip test on CRAN diff --git a/tests/testthat/test-set_subscription_key.R b/tests/testthat/test-set_subscription_key.R new file mode 100644 index 0000000..f489883 --- /dev/null +++ b/tests/testthat/test-set_subscription_key.R @@ -0,0 +1,15 @@ +test_that("DTM API subscription key set as environmental variable.", { + skip_on_cran() + + env_var_before_call <- Sys.getenv()["DTM_SUBSCRIPTION_KEY"] |> as.vector() + + subscription_key_input <- "dummyValue" + set_subscription_key(key = subscription_key_input) + + # as.vector() is used to remove all attributes including names. + env_var_after_call <- Sys.getenv()["DTM_SUBSCRIPTION_KEY"] |> as.vector() + # Restore old env variable value to what it was before the function was tested. + Sys.setenv("DTM_SUBSCRIPTION_KEY" = env_var_before_call) + + expect_true(env_var_after_call == subscription_key_input) +}) diff --git a/vignettes/.gitignore b/vignettes/.gitignore index 097b241..9e2bd63 100644 --- a/vignettes/.gitignore +++ b/vignettes/.gitignore @@ -1,2 +1,4 @@ *.html *.R + +/.quarto/ diff --git a/vignettes/introduction.Rmd b/vignettes/introduction.Rmd index 9ce40b5..1fd4efc 100644 --- a/vignettes/introduction.Rmd +++ b/vignettes/introduction.Rmd @@ -12,7 +12,6 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) -library(httr2) ``` ## Introduction @@ -29,26 +28,54 @@ The `dtmapi` package provides functions to interact with the Displacement Trac - `get_idp_admin2_data()` - ## Install Package -The `dtmapi` package is available on CRAN and can be installed using the following command: : -```{r install , eval = FALSE} +The `dtmapi` package is available on CRAN and can be installed using the following command: +```{r install, eval = FALSE} install.packages("dtmapi") ``` ## Load Package After installation, load the package using library(): -```{r setup} +```{r setup, eval = FALSE} library(dtmapi) ``` +## Setting the Subscription Key +After creating a subscription key on https://dtm-apim-portal.iom.int, that key +needs to be given to R. There are two main ways to do this. + +1. By calling `set_subscription_key()` and then typing in the key. + +The user will be prompted to input the key into a pop-up field. +```{r set_subscription_key_demo, eval = FALSE} +set_subscription_key() +``` + +2. By setting up the environment variable directly. + +If one would like to avoid re-inputting the subscription key for each R session, +an alternative approach is to create the environment variable +`DTM_SUBSCRIPTION_KEY`, with the subscription key as its value. Ideally, this +should be (a) done in a *.Renviron* file, and (b) through the use of *R projects* +or other means of organising files (e.g. if one is using VS Code: *workspaces*). + +One quick way to get started is by calling the following line of code, if one is +using an R project or equivalent: +```{r r_environ_demo_project, eval = FALSE} +usethis::edit_r_environ("project") +``` +Or, if one is not using an R project or equivalent: +```{r r_environ_demo_user, eval = FALSE} +usethis::edit_r_environ("user") +``` + ## Get All Countries The `get_all_countries()` function retrieves a list of all countries from the DTM API. -```{r get_country} +```{r get_country, eval = FALSE} # Fetch all countries countries_df <- get_all_countries() @@ -60,7 +87,7 @@ head(countries_df) The `get_all_operations()` function retrieves a list of all operations from the DTM API. -```{r get_operations} +```{r get_operations, eval = FALSE} # Fetch all operations operations_df <- get_all_operations() @@ -72,7 +99,7 @@ head(operations_df) The `get_idp_admin0_data()` function retrieves Internally Displaced Persons (IDP) data aggregated at the country level. -```{r get_idp_admin0} +```{r get_idp_admin0, eval = FALSE} # Fetch IDP data at Admin Level 0 idp_admin0_df <- get_idp_admin0_data(CountryName='Ethiopia', FromRoundNumber=1, ToRoundNumber=10) @@ -84,7 +111,7 @@ head(idp_admin0_df) The get_idp_admin1_data() function retrieves IDP data aggregated at Admin Level 1. -```{r get_idp_admin1} +```{r get_idp_admin1, eval = FALSE} # Fetch IDP data at Admin Level 1 idp_admin1_df <- get_idp_admin1_data(CountryName='Sudan', Admin1Name="Blue Nile", FromReportingDate='2020-01-01', ToReportingDate='2024-08-15') @@ -95,7 +122,7 @@ head(idp_admin1_df) ## Get IDP Data at Admin Level 2 The get_idp_admin2_data() function retrieves IDP data aggregated at Admin Level 2 -```{r get_idp_admin2} +```{r get_idp_admin2, eval = FALSE} # Fetch IDP data at Admin Level 2 idp_admin2_df <- get_idp_admin2_data(Operation="Displacement due to conflict", CountryName='Lebanon')