R/get_norman_data.R

Defines functions get_norman_data

Documented in get_norman_data

#' Retrieve Data from Norman Network REST API
#'
#' This function interacts with the Norman Network Database System (NDS) API.
#'
#' @param module A character string specifying the database module.
#'   Allowed values:
#'   \itemize{
#'     \item \code{"susdat"} - Substance Database
#'     \item \code{"ecotox"} - Ecotoxicology Database
#'     \item \code{"empodat"} - EMPODAT Database
#'     \item \code{"passive"} - Passive Sampling Database
#'   }
#'
#' @param parameter A character string specifying the search parameter.
#'   The allowed parameters depend on the selected \code{module}:
#'   \itemize{
#'     \item For \code{module = "susdat"}: \code{"nsid"}, \code{"casrn"}, \code{"inchikey"}
#'     \item For \code{module = "ecotox"}: \code{"nsid"}, \code{"casrn"}, \code{"inchikey"}
#'     \item For \code{module = "empodat"}: \code{"nsid"}, \code{"casrn"}, \code{"inchikey"}, \code{"country"}, \code{"matrix"}, \code{"id"}
#'     \item For \code{module = "passive"}: \code{"nsid"}, \code{"casrn"}, \code{"inchikey"}, \code{"country"}, \code{"matrix"}
#'   }
#'   \strong{Parameter Descriptions:}
#'   \itemize{
#'     \item \code{nsid}: Norman SusDat ID (e.g., "NS00001027" or "1027")
#'     \item \code{casrn}: CAS Registry Number (e.g., "1490-04-6")
#'     \item \code{inchikey}: International Chemical Identifier Key (e.g., "NOOLISFMXDJSKH-UHFFFAOYSA-N")
#'     \item \code{country}: Country Alpha-2 code (e.g., "SK")
#'     \item \code{matrix}: Ecosystem/matrix ID (e.g., "3")
#'     \item \code{id}: Empodat ID or range (e.g., "100" or "100:150")
#'   }
#'
#' @param value A character or numeric value corresponding to the chosen \code{parameter}.
#'
#' @param page (Optional) Integer or character. The page number for pagination.
#'   If \code{NULL} (default), the page segment is omitted from the URL.
#'
#' @param format A character string specifying the output format.
#'   Allowed values: \code{"json"}, \code{"xml"}. Defaults to \code{"json"}.
#'
#' @return
#'   \itemize{
#'     \item If \code{format = "json"}: A list or data frame (parsed JSON).
#'     \item If \code{format = "xml"}: A raw character string (XML content).
#'   }
#'
# To-Do: consider extended examples for fetch_norman()
# \dontrun{
#   # Example 1: Get substance data by CAS number (JSON)
#   data_cas <- get_norman_data(
#     module = "susdat",
#     parameter = "casrn",
#     value = "1490-04-6"
#   )
# 
#   # Example 2: Get EMPODAT data by Matrix ID with pagination (Page 1)
#   data_matrix <- get_norman_data(
#     module = "empodat",
#     parameter = "matrix",
#     value = "3",
#     page = 1
#   )
# 
#   # Example 3: Get EMPODAT data by ID range
#   data_range <- get_norman_data(
#     module = "empodat",
#     parameter = "id",
#     value = "100:150"
#   )
# }
# @export
get_norman_data <- function(module, parameter, value, page = NULL, format = "json") {
  
  # --- 1. Input Validation ---
  
  # Define allowed modules and their corresponding parameters
  # This map ensures strictly valid API calls based on documentation
  valid_map <- list(
    susdat   = c("nsid", "casrn", "inchikey"),
    ecotox   = c("nsid", "casrn", "inchikey"),
    empodat  = c("nsid", "casrn", "inchikey", "country", "matrix", "id"),
    passive  = c("nsid", "casrn", "inchikey", "country", "matrix")
  )
  
  # Validate 'module'
  if (!module %in% names(valid_map)) {
    stop(paste0(
      "Error: Invalid module '", module, "'.\n",
      "Allowed modules: ", paste(names(valid_map), collapse = ", ")
    ))
  }
  
  # Validate 'parameter' based on the selected 'module'
  allowed_params <- valid_map[[module]]
  if (!parameter %in% allowed_params) {
    stop(paste0(
      "Error: Invalid parameter '", parameter, "' for module '", module, "'.\n",
      "Allowed parameters for this module: ", paste(allowed_params, collapse = ", ")
    ))
  }
  
  # Validate 'format'
  if (!format %in% c("json", "xml")) {
    stop("Error: Invalid format. Allowed values are 'json' or 'xml'.")
  }
  
  # --- 2. Request Construction ---
  
  base_url <- "https://www.norman-network.com/nds/api"
  
  # Initialize the request object using httr2
  req <- httr2::request(base_url)
  
  # Append path segments dynamically
  # Logic:
  # If page is NULL: /module/parameter/value/format
  # If page is NOT NULL: /module/parameter/value/page/format
  if (is.null(page)) {
    req <- req |>
      httr2::req_url_path_append(module, parameter, value, format)
  } else {
    req <- req |>
      httr2::req_url_path_append(module, parameter, value, page, format)
  }
  
  # Add User-Agent header (Good practice for API clients)
  req <- req |>
    httr2::req_user_agent("R_Norman_Package/1.0 (Integration)") |>
    httr2::req_retry(max_tries = 3)
  
  # --- 3. Execution and Error Handling ---
  
  # Perform the request with error handling
  tryCatch({
    resp <- httr2::req_perform(req)
  }, error = function(e) {
    stop(paste("API Request Failed:", e$message))
  })
  
  # Check for non-200 status codes (though req_perform usually handles this, explicit check is safe)
  if (httr2::resp_status(resp) != 200) {
    stop(paste("API returned error status:", httr2::resp_status(resp)))
  }
  
  # --- 4. Response Parsing ---
  
  # Return data based on requested format
  if (format == "json") {
    # Parse JSON into an R list/dataframe
    return(httr2::resp_body_json(resp)) #, simplifyVector = TRUE))
  } else {
    # For XML, return the raw string (user can parse with xml2 if needed)
    return(httr2::resp_body_string(resp))
  }
}

Try the normanR package in your browser

Any scripts or data that you put into this service are public.

normanR documentation built on Sept. 12, 2026, 5:10 p.m.