R/auth.R

Defines functions get_os kgl_auth_file_setup kgl_auth

Documented in kgl_auth kgl_auth_file_setup

#' Kaggle API authorization
#'
#' Authorizing access to Kaggle's API
#'
#' @inheritParams httr2::req_auth_basic
#' @param username Name associated with user's account at
#'   \href{https://www.kaggle.com}{Kaggle.com}.
#' @param key Key (a string of random letters and numbers) generated by Kaggle. Users can
#'   obtain or lookup their keys by signing into Kaggle and clicking on 'Account" or by
#'   substituting their username at kaggle.com/{username}/account.
#' @param creds_file Path to user's \code{kaggle.json} file. See @details for
#'   more information.
#'
#' @details
#' When Kaggle generates a key, it's sent to users as a
#' \code{kaggle.json} file, which is likely saved in a place like the default
#' Downloads folder or something. Users can (a) save the \code{kaggle.json}
#' file to a \code{.kaggle} directory in the user's machine's home folder
#' (i.e.,  \code{~/.kaggle/kaggle.json}), (b) specify the path to their
#' \code{kaggle.json} file using this \code{creds_file} argument, or (c)
#' save their \code{kaggle.json} file in their current working directory. The
#' third options (options c) is the least desirable as your working directory
#' will presumably change between sessions where you'd still want to have
#' authorized access to Kaggle's API. Either of the first two options would be
#' a good choice.
#'
#' Kaggle encourages users to save the "kaggle.json" credentials file in their
#' .kaggle file in their home directory (\code{~/.kaggle/kaggle.json}). If all
#' arguments are left NULL (their defaults), this function will look to see if
#' you computer has a \code{~/.kaggle/kaggle.json} file. If it can't find it
#' there, it'll look in the current working directory. If it finds the file,
#' it'll use it. Otherwise it'll look for environment variables, which it will
#' create and save for you manually the first time you enter your username/key
#' or path to your \code{kaggle.json} file.
kgl_auth <- function(req = NULL, username = NULL, key = NULL, creds_file = NULL) {
  if (get_os() == "windows") {
    path_cred_os <- fs::path("C:/Users/", Sys.getenv("USERNAME"), ".kaggle", "kaggle.json")
  } else {
    path_cred_os <- fs::path("~/.kaggle/kaggle.json")
  }

  ## if all null, look for environment variables
  if (is.null(username) && is.null(key) && is.null(creds_file)) {
    if (file.exists(path_cred_os)) {
      creds_file <- path_cred_os
    } else if (file.exists("kaggle.json")) {
      creds_file <- "kaggle.json"
    }
  }

  if (is.null(username) && is.null(key) && is.null(creds_file)) {
    pat <- Sys.getenv("KAGGLE_PAT")
    if (!identical(pat, "")) {
      username <- sub("[/\\s,;].*", "", pat)
      key <- sub(".*[/\\s,;]", "", pat)
    } else {
      username <- Sys.getenv("KAGGLE_USERNAME")
      key <- Sys.getenv("KAGGLE_KEY")
    }
    if (identical(username, "") || identical(key, "")) {
      stop(
        "Couldn't find environment variables. Please set\nKAGGLE_PAT=",
        "{username}/{key}\n# or\nKAGGLE_USERNAME={username}\nKAGGLE_KEY={key}"
      )
    }

    ## alternatively, users can provide path to (or text of) creds_file
  } else if (!is.null(creds_file)) {
    ## read and parse kaggle.json creds file if it exists
    if (file.exists(creds_file)) {
      con <- file(creds_file)
      creds_file <- readLines(con, warn = FALSE, encoding = "UTF-8")
      close(con)

      ## if file doens't exist and if contents are NOT supplied as a string then STOP
    } else if (!grepl("username.*key", creds_file)) {
      stop(
        "Kaggle credentials .json file not found. Provide path to file OR ",
        "enter your Kaggle 'username' and 'key' (from kaggle.com/{username}/account.",
        call. = FALSE
      )
    }

    ## parse the username and key
    username <- regmatches(
      creds_file,
      regexpr(
        "(?<=username\":\")[^\"]+",
        creds_file,
        perl = TRUE
      )
    )
    key <- regmatches(
      creds_file,
      regexpr(
        "(?<=key\":\")[^\"]+",
        creds_file,
        perl = TRUE
      )
    )

    ## validate username and key inputs
    stopifnot(
      length(username) == 1,
      is.atomic(username),
      length(key) == 1,
      is.character(key)
    )

    ## set as store as env variable
    set_renv(KAGGLE_PAT = paste0(username, "/", key))
  } else {
    ## validate username and key inputs
    stopifnot(
      length(username) == 1,
      is.atomic(username),
      length(key) == 1,
      is.character(key)
    )

    ## set as store as env variable
    set_renv(KAGGLE_PAT = paste0(username, "/", key))

    ## KAGGLE_USERNAME
    ## KAGGLE_KEY
    # set_renv(KAGGLE_USERNAME = username)
    # set_renv(KAGGLE_KEY = key)
    message(
      "Your Kaggle key has been recorded for this session and saved as `KAGGLE_PAT`\n",
      "  environment variable for future sessions."
    )
  }

  ## return basic http authorization method (with kaggle-generated key as password)
  if (is.null(req)) {
    httr::authenticate(username, key)
  } else {
    httr2::req_auth_basic(req, username, key)
  }
}

#' Setup API authorization file
#'
#' This function aims to simplify the authorization setup process. Once the API key has been generated and the `kaggle.json` file is saved, use the path of the downloaded file to have it correctly stored in the home directory. This function will only need to be ran once per API key that is generated.
#'
#' @param path Character. Path to `kaggle.json`
#'
#' @return Nothing.
#' @export
#' @family Authorization
#'
#' @examples
#' \dontrun{
#' kgl_auth_file_setup("/Users/Kow/Downloads/kaggle.json")
#' }
kgl_auth_file_setup <- function(path) {
  if (.Platform$OS.type == "windows") {
    dir_kgl_home <- fs::path("C:/Users/", Sys.getenv("USERNAME"), ".kaggle")
  } else {
    dir_kgl_home <- fs::path("~/.kaggle")
  }

  path_kgl_home <- fs::path(dir_kgl_home, "kaggle.json")
  path_kgl_home_ui <- usethis::ui_value(path_kgl_home)

  if (!fs::dir_exists(dir_kgl_home)) {
    fs::dir_create(dir_kgl_home)
  }

  if (fs::file_exists(path_kgl_home)) {
    user_prompt <- usethis::ui_yeah("The file {path_kgl_home_ui} already exists. Only overwrite this file if you have generated a new API key. Overwrite?")

    if (user_prompt) {
      fs::file_delete(path_kgl_home)
    } else {
      usethis::ui_done("No action was taken.")
      return(invisible())
    }
  }

  fs::file_copy(
    path = path,
    new_path = path_kgl_home
  )

  usethis::ui_done("File has been copied to {path_kgl_home_ui}!")
  usethis::ui_todo("Be sure to run {usethis::ui_value('kgl_auth()')} to setup environment variables!")

  return(invisible())
}

#' Get current OS
#'
#' @return Character displaying current OS.
#'
#' @references [Source code](https://conjugateprior.org/2015/06/identifying-the-os-from-r/)
#'
#' @noRd
get_os <- function() {
  sysinf <- tolower(Sys.info())

  if (!is.null(sysinf)) {
    os <- sysinf["sysname"]
    if (os == "Darwin") {
      os <- "osx"
    }
  } else { ## mystery machine
    os <- .Platform$OS.type
    if (grepl("^darwin", R.version$os)) {
      os <- "osx"
    }
    if (grepl("linux-gnu", R.version$os)) {
      os <- "linux"
    }
  }

  return(os)
}
KoderKow/kaggler documentation built on Aug. 26, 2023, 11:27 a.m.