diff --git a/R/random_uuid.R b/R/random_uuid.R new file mode 100644 index 0000000..bc90ea4 --- /dev/null +++ b/R/random_uuid.R @@ -0,0 +1,23 @@ +#' Generate random UUID +#' +#' @param seed Integer to set seed for random sampling. Default value is NULL. +#' +#' @return A single character value in the format of eight blocks of four +#' letters/integers separated by dashes. +#' +#' @noRd + +random_uuid <- function(seed = NULL) { + + options <- c(letters, 0:9) + + # There should be equal probability of a letter or integer being sampled + prob_weights <- c(rep(1/26, 26), rep(1/10, 10)) + + set.seed(seed) + + replicate(8, paste0(sample(options, 4, replace = TRUE, prob = prob_weights), + collapse = "")) |> + paste0(collapse = "-") + +} diff --git a/README.md b/README.md index 8c8c06c..2489b24 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,7 @@ -objectiveR is an R package wrapper for the [Objective Connect API](https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/). - +objectiveR aims to provide a convenient method of interacting with [Objective Connect](https://secure.objectiveconnect.co.uk) using R, making use of the [Objective Connect API](https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html). ## Installation diff --git a/_pkgdown.yml b/_pkgdown.yml index 849e414..5c990c6 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -1,7 +1,12 @@ url: https://scotgovanalysis.github.io/objectiveR +development: + mode: auto + template: bootstrap: 5 + bslib: + base_font: {google: "Roboto"} home: sidebar: diff --git a/tests/testthat/test-random_uuid.R b/tests/testthat/test-random_uuid.R new file mode 100644 index 0000000..91d5b01 --- /dev/null +++ b/tests/testthat/test-random_uuid.R @@ -0,0 +1,34 @@ + +test_that("Single character value returned", { + + expect_type(random_uuid(), "character") + + expect_length(random_uuid(), 1) + +}) + +test_that("Returned value format correct", { + + pattern_exp <- paste(rep("[a-z0-9]{4}", 8), collapse = "-") + + expect_true(grepl(pattern = pattern_exp, x = random_uuid())) + +}) + +test_that("Not setting seed returns random value", { + + sample_5 <- replicate(5, random_uuid()) + + expect_true(length(unique(sample_5)) == 5) + +}) + +test_that("Setting seed returns consistent value", { + + expect_identical( + random_uuid(1234), + random_uuid(1234) + ) + +}) + diff --git a/vignettes/auth_prompt.png b/vignettes/auth_prompt.png new file mode 100644 index 0000000..73544ce Binary files /dev/null and b/vignettes/auth_prompt.png differ diff --git a/vignettes/authentication.Rmd b/vignettes/authentication.Rmd new file mode 100644 index 0000000..c9a0382 --- /dev/null +++ b/vignettes/authentication.Rmd @@ -0,0 +1,87 @@ +--- +title: "Authentication" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Authentication} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + eval = FALSE +) +``` + +## How authentication works + +The first time you send a request, the API requires your Objective Connect user email address and password to authenticate. + +Each successful response from the API includes a token. This token can then be used to authenticate subsequent requests in your session, negating the need to repeatedly supply your email address and password. + +The rest of this article details how to manage authentication when using the `objectiveR` package. + + +## First request + +You will need your Objective Connect user email address and password to make a first request to the API. These can be provided in two ways. + +### R Environment variables + +Add two variables to your `.Renviron` file to define your email address and password. + +* Open your `.Renviron` file to edit: + + ```{r open_renviron} + usethis::edit_r_environ() + ``` + +* Add two variables as follows: + + ```{r add_vars} + OBJECTIVER_USR = "XXX" + OBJECTIVER_PWD = "XXX" + ``` + +* Save and close the `.Renviron` file. + +* To check this has worked as expected, first restart your R session then: + + ```{r check_vars} + Sys.getenv("OBJECTIVER_USR") + Sys.getenv("OBJECTIVER_PWD") + ``` + + Your credentials should be printed in the console. + + Note: For this reason, it is important not to save your R session workspace on close as your console may contain your Objective Connect credentials. + +The benefit of this method is that you can leave this information in your `.Renviron` file and `objectiveR` will automatically find them here each time you use the package. + + +### Supply credentials when prompted + +If you don't have these variables defined in your `.Renviron` file, `objectiveR` will prompt you to supply them if you're working in an interactive session. + +
+ +```{r} +#| eval: true +#| echo: false +#| fig-align: "center" +#| fig-alt: > +#| A small pop-up window with prompt 'Enter email registered with Objective +#| Connect' followed by a text input box and buttons to select 'OK' or +#| 'Cancel'. +knitr::include_graphics("auth_prompt.png", dpi = "retina") +``` + + +## Subsequent requests + +Each successful API response includes a token that can be used for subsequent requests. `objectiveR` functions automatically parse this token from the API response and store it in your R session's global environment. + +Where this object exists, `objectiveR` will automatically use it to authenticate subsequent requests. + +If this object doesn't exist, `objectiveR` will resort to using your username and password, as it did for your [first request](#first-request). + diff --git a/vignettes/objectiveR.Rmd b/vignettes/objectiveR.Rmd new file mode 100644 index 0000000..7c030a2 --- /dev/null +++ b/vignettes/objectiveR.Rmd @@ -0,0 +1,143 @@ +--- +title: "Introduction to objectiveR" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Introduction to objectiveR} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + eval = FALSE +) +``` + +objectiveR aims to provide a convenient method of interacting with [Objective Connect](https://secure.objectiveconnect.co.uk) using R, making use of the [Objective Connect API](https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html). + +This article demonstrates a simple workflow for using the package, with the ultimate aim of downloading or uploading a file to an Objective Connect workspace. + +Start by loading the package in your R session: + +```{r setup} +library(objectiveR) +``` + + +## Universally Unique Identifiers + +It is useful to be aware that everything on Objective Connect is associated with a Universally Unique Identifier (UUID). This includes users, workgroups, workspaces, participants in a workspace and assets in a workspace. + +Objective Connect UUIDs take the form of a string of eight chunks of four lower case letters and numbers, separated by dashes. For example: + +``` +84op-9qdu-c692-t4z1-wa4z-h9k3-8454-i71f +``` + +Use of the API and the objectiveR package depends on these UUIDs. UUIDs are mostly findable using the functions in objectiveR (as demonstrated in this article), however you can often also find relevant UUIDs in the webpage URLs. For example, this is an example URL when viewing a workspace: + +``` +https://secure.objectiveconnect.co.uk/share/2vo2-dd3s-1nn9-y20r-b906-u4s2- +7134-b352?workgroupUuid=2j47-ff38-lcgg-mnis-3vq8-9536-9jfp-oy44 +``` + +The UUID for the workspace is `2vo2-dd3s-1nn9-y20r-b906-u4s2-7134-b352` and the UUID for the workgroup is `2j47-ff38-lcgg-mnis-3vq8-9536-9jfp-oy44`. + + +## Test your authentication + +To interact with the Objective Connect API, you must provide valid authentication. This is explained in more detail in `vignette("authentication")`. + +It might be a good idea to test your authentication, especially if it's your first time using the package. There is no specific function for this, but the following simple function to get your own user ID requires no input and is an easy way to test that you can get a successful response from the API. + +```{r user} +me <- my_user_id() +``` + + +## Workspaces + +To see workspaces you are a member of: + +```{r workspaces} +workspaces <- my_workspaces() +``` + +This returns a data frame with a row for each workspace you are a member of. Among other things, there will be a column containing the workspace name and another containing the workspace UUID. For example: + +```{r workspaces-output, eval = TRUE, echo = FALSE} +data.frame( + workspace_name = c("Project 1", "Project 2"), + workspace_uuid = sapply(1:2, objectiveR:::random_uuid) +) +``` + + +## Assets + +Maybe you would like to download a file from one of your workspaces. In Objective Connect, files are also known as 'assets'. To download an asset, you'll need its UUID. + +Use the workspace UUID from `workspaces` to get a data frame of assets in the workspace: + +```{r assets} +assets <- workspace_assets("84op-9qdu-c692-t4z1-wa4z-h9k3-8454-i71f") +``` + +This returns a data frame with a row for each asset in the workspace. Among other things, there will be a column containing the asset name and another containing the asset UUID. + +```{r assets-output, eval = TRUE, echo = FALSE} +data.frame( + uuid = sapply(3:4, objectiveR:::random_uuid), + name = c("asset1", "asset2") +) +``` + + +## Download file + +To download a document, use its UUID and the file path of the folder you'd like the downloaded file to be saved to: + +```{r download} +download_file(document_uuid = "2j47-ff38-lcgg-mnis-3vq8-9536-9jfp-oy44", + folder = here::here("data")) +``` + +This function doesn't return anything but will display a message in the R console to indicate the download has been a success and to confirm the location of the file: + +```{r download-output, eval = TRUE, echo = FALSE, message = TRUE} +cli::cli_alert_success( + "File downloaded: project-1/data/file1.csv." +) +``` + + +## Upload file + +To upload a file to a workspace, use the UUID of the workspace and the file path of the file to upload: + +```{r upload} +new_document(here::here("data", "file2.csv"), + workspace_uuid = "84op-9qdu-c692-t4z1-wa4z-h9k3-8454-i71f") +``` + +This function doesn't return anything but will display a message in the R console to indicate the upload has been a success and to confirm the name of the file. + +```{r upload-output, eval = TRUE, echo = FALSE, message = TRUE} +cli::cli_alert_success( + "New document created: file2.csv." +) +``` + +Now, if you rerun the previous step to get a data frame of workspace assets, you should see an additional row for the file you've just uploaded. + +```{r assets2} +assets <- workspace_assets("84op-9qdu-c692-t4z1-wa4z-h9k3-8454-i71f") +``` + +```{r assets2-output, eval = TRUE, echo = FALSE} +data.frame( + uuid = sapply(3:5, objectiveR:::random_uuid), + name = c("Asset 1", "Asset 2", "file2") +) +``` +