Build libraries for Google APIs with OAuth2 for both local and Shiny app use.
This guide is also available at the googleAuthR website
Here is a list of available Google APIs. this library can be used with.
These libraries are all compatible as they use googleAuthR
for authentication backend e.g. can use just one OAuth2 login flow and are Shiny compatible.
Feel free to add your own via email or a pull request if you have used googleAuthR to build something cool.
GoogleAuthR version 0.1.2 is now available on CRAN
install.packages("googleAuthR")
If you want to use the development version on Github, install via:
## load the library or download it if necessary
if(!require(googleAuthR)){
if(!require(devtools)){
install.packages("devtools")
} else {
devtools::install_github("MarkEdmondson1234/googleAuthR")
}
}
library(googleAuthR)
This guide is available at: vignette("googleAuthR")
This library allows you to authenticate easily via local use in an OAuth2 flow; within a Shiny app; or via service accounts.
The main two functions are gar_auth()
and gar_api_generator()
.
gar_auth
This takes care of getting the authentication token, storing it and refreshing. Use it before any call to a Google library.
gar_api_generator
This creates functions for you to use to interact with Google APIs. Use it within your own function definitions, to query the Google API you want.
googleAuthR
has a default project setup with APIs activated for several APIs, but it is recommended you use your own Client IDs as the login screen will be big and scary for users with so many APIs to approve.
It is preferred to configure your functions to only use the scopes they need. Scopes you need will be specified in the Google API documentation.
Set scopes via the option googleAuthR.scopes.selected
.
The below example sets scopes for Search Console, Google Analytics and Tag Manager:
options("googleAuthR.scopes.selected" = c("https://www.googleapis.com/auth/webmasters",
"https://www.googleapis.com/auth/analytics",
"https://www.googleapis.com/auth/tagmanager.readonly"))
googleAuthR
has been loaded:
options("googleAuthR.client_id" = "YOUR_CLIENT_ID")
options("googleAuthR.client_secret" = "YOUR_CLIENT_SECRET")
https://mark.shinyapps.io/searchConsoleRDemo/
127.0.0.1
with a port number for local testing. Remember the port number you use as you will need it later to launch the app e.g. http://127.0.0.1:1221
options("googleAuthR.webapp.client_id" = "YOUR_CLIENT_ID")
options("googleAuthR.webapp.client_secret" = "YOUR_CLIENT_SECRET")
shiny::runApp(port=1221)
options("googleAuthR.scopes.selected" =
c("https://www.googleapis.com/auth/urlshortener"))
If the above is successful, then you should go through the Google login flow in your browser when you run this command:
googleAuthR::gar_auth()
If you ever need to authenticate with a new user, use:
googleAuthR::gar_auth(new_user=TRUE)
Authentication token is cached in a hidden file called .httr-oauth
in the working directory.
If for some reason you need authentication without access to a browser (for example when using Shiny Server), then you can authenticate locally and upload the .httr-oauth
file to the folder of your script.
If you want to create a Shiny app just using your data, upload the app with your own .httr-oauth
.
If you want to make a multi-user Shiny app, where users login to their own Google account and the app works with their data, googleAuthR provides these functions to help make the Google login process as easy as possible.
As of 0.3.0 googleAuthR uses Shiny Modules. This means less code and the ability to have multiple login buttons on the same app.
googleAuth
- creates the authentication token and login button stylinggoogleAuthUI
- creates the server side login button for users to authenticate with.with_shiny()
- wraps your API functions so they can be passed the user's authentication token.library(shiny)
library(googleAuthR)
options("googleAuthR.scopes.selected" = c("https://www.googleapis.com/auth/urlshortener"))
shorten_url <- function(url){
body = list(
longUrl = url
)
f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
"POST",
data_parse_function = function(x) x$id)
f(the_body = body)
}
## server.R
server <- function(input, output, session){
## Create access token and render login button
access_token <- callModule(googleAuth, "loginButton")
short_url_output <- eventReactive(input$submit, {
## wrap existing function with_shiny
## pass the reactive token in shiny_access_token
## pass other named arguments
with_shiny(f = shorten_url,
shiny_access_token = access_token(),
url=input$url)
})
output$short_url <- renderText({
short_url_output()
})
}
## ui.R
ui <- fluidPage(
googleAuthUI("loginButton"),
textInput("url", "Enter URL"),
actionButton("submit", "Shorten URL"),
textOutput("short_url")
)
shinyApp(ui = ui, server = server)
You can also authenticate single users via a server side JSON file rather than going through the online OAuth2 flow. The end user could supply this JSON file, or you can upload your own JSON file to your applications.
This involves downloading a secret JSON key with the authentication details. More details are available from Google here: Using OAuth2.0 for Server to Server Applications[https://developers.google.com/identity/protocols/OAuth2ServiceAccount]
To use, go to your Project in the Google Developement Console and select JSON Key type. Save the JSON file to your computer and supply the file location to the function
gar_auth_service()
Navigate to the JSON file from the Google Developer Console via:
Credentials > New credentials > Service account Key > Select service account > Key type = JSON
An example using a service account JSON file for authentication is shown below:
library(googleAuthR)
service_token <- gar_auth_service(json_file="~/location/of/the/json/secret.json")
analytics_url <- function(shortUrl,
timespan = c("allTime", "month", "week","day","twoHours")){
timespan <- match.arg(timespan)
f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
"GET",
pars_args = list(shortUrl = "shortUrl",
projection = "FULL"),
data_parse_function = function(x) {
a <- x$analytics
return(a[timespan][[1]])
})
f(pars_arguments = list(shortUrl = shortUrl))
}
analytics_url("https://goo.gl/2FcFVQbk")
From version 0.3.0
a RStudio Addin is available via the RStudio Addin menu once you load the package, or via googleAuthR:::gar_gadget()
It lets you set the scopes and then saves you some typing by calling the Google authentication flow for you.
For local use, delete the .httr-oauth
file.
For service level accounts delete the JSON file.
For a Shiny app, a cookie is left by Google that will mean a faster login next time a user uses the app with no Authorization screen that they get the first time through. To force this every time, activate the parameter revoke=TRUE
within the renderLogin
function. Example below:
shinyServer(function(input, output, session)){
## Get auth code from return URL
access_token <- reactiveAccessToken(session)
## Make a loginButton to display using loginOutput
## revoke=TRUE means upon logout a user will need to reauthenticate
output$loginButton <- renderLogin(session, access_token(), revoke=TRUE)
## Needed if revoke=TRUE above
revokeEventObserver(access_token())
...
}
## in ui.R
library(shiny)
library(googleAuthR)
shinyUI(
fluidPage(
loginOutput("loginButton"),
....
)
))
}
Creating your own API should then be a matter of consulting the Google API documentation, and filling in the required details.
gar_api_generator
has these components:
baseURI
- all APIs have a base for every API callhttp_header
- what type of request, most common are GET and POSTpath_args
- some APIs need you to alter the URL folder structure when calling, e.g. /account/{accountId}/
where accountId
is variable.pars_args
- other APIS require you to send URL parameters e.g. ?account={accountId}
where accountId
is variable.data_parse_function
- [optional] If the API call returns data, it will be available in $content
. You can create a parsing function that transforms it in to something you can work with (for instance, a dataframe)Example below for generating a function:
f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
"POST",
data_parse_function = function(x) x$id)
The function generated uses path_args
and pars_args
to create a template, but when the function is called you will want to pass dynamic data to them. This is done via the path_arguments
and pars_arguments
parameters.
path_args
and pars_args
and path_arguments
and pars_arguments
all accept named lists.
If a name in path_args
is present in path_arguments
, then it is substituted in. This way you can pass dynamic parameters to the constructed function. Likewise for pars_args
and pars_arguments
.
## Create a function that requires a path argument /accounts/{accountId}
f <- gar_api_generator("https://www.googleapis.com/example",
"POST",
path_args = list(accounts = "defaultAccountId")
data_parse_function = function(x) x$id)
## When using f(), pass the path_arguments function to it
## with the same name to modify "defaultAccountId":
result <- f(path_arguments = list(accounts = "myAccountId"))
A lot of Google APIs look for you to send data in the Body of the request. This is done after you construct the function.
googleAuthR
uses httr
's JSON parsing via jsonlite
to construct JSON from R lists.
Construct your list, then use jsonlite::toJSON
to check if its in the correct format as specified by the Google documentation. This is often the hardest part using the API.
Not all API calls return data, but if they do:
If you have no data_parse_function
then the function returns the whole request object. The content is available in $content
. You can then parse this yourself, or pass a function in to do it for you.
If you parse in a function into data_parse_function
, it works on the response's $content
.
Example below of the differences between having a data parsing function and not:
## the body object that will be passed in
body = list(
longUrl = "http://www.google.com"
)
## no data parsing function
f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
"POST")
no_parse <- f(the_body = body)
## parsed data, only taking request$content$id
f2 <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
"POST",
data_parse_function = function(x) x$id)
parsed <- f2(the_body = body)
## str(no_parse) has full details of API response.
## just looking at no_parse$content as this is what API returns
> str(no_parse$content)
List of 3
$ kind : chr "urlshortener#url"
$ id : chr "http://goo.gl/ZwT9pG"
$ longUrl: chr "http://www.google.com/"
## compare to the above - equivalent to no_parse$content$id
> str(parsed)
chr "http://goo.gl/mCYw2i"
The response is turned from JSON to a dataframe if possible, via jsonlite::fromJSON
In some cases you may want to skip all parsing of API content, perhaps if it is not JSON or some other reason.
For these cases, you can use the option option("googleAuthR.rawResponse" = TRUE)
to skip all tests and return the raw response.
If you are doing many API calls, you can speed this up a lot by using the batch option.
This takes the API functions you have created and wraps them in the gar_batch
function to request them all in one POST call. You then recieve the responses in a list.
Note that this does not count as one call for API limits purposes, it just speeds up the processing.
The example below queries from two different APIs and returns them in a list: IT lists websites in your Google Search Console, and shows your goo.gl link history.
## from search console API
list_websites <- function() {
l <- gar_api_generator("https://www.googleapis.com/webmasters/v3/sites",
"GET",
data_parse_function = function(x) x$siteEntry)
l()
}
## from goo.gl API
user_history <- function(){
f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url/history",
"GET",
data_parse_function = function(x) x$items)
f()
}
googleAuthR::gar_auth(new_user=T)
ggg <- gar_batch(list(list_websites(), user_history()))
A common batch task is to walk through the same API call, modifying only one parameter. An example includes walking through Google Analytics API calls by date to avoid sampling.
A function to enable this is implemented at gar_batch_walk
, with an example below:
walkData <- function(ga, ga_pars, start, end){
dates <- as.character(
seq(as.Date(start, format="%Y-%m-%d"),
as.Date(end, format="%Y-%m-%d"),
by=1))
ga_pars$samplingLevel <- "HIGHER_PRECISION"
anyBatchSampled <- FALSE
samplePercent <- 0
## this is applied to each batch to keep tally of meta data
bf <- function(batch_data){
lapply(batch_data, function(the_data) {
if(attr(the_data, 'containsSampledData')) anyBatchSampled <<- TRUE
samplePercent <<- samplePercent + attr(the_data, "samplePercent")
})
batch_data
}
## the walk through batch function.
## In this case both start-date and end-date are set to the date iteration
## if the output is parsed as a dataframe, it also includes a rbind function
## otherwise, it will return a list of lists
walked_data <- googleAuthR::gar_batch_walk(ga,
dates,
gar_pars = ga_pars,
pars_walk = c("start-date", "end-date"),
batch_function = bf,
data_frame_output = TRUE)
message("Walked through all dates. Total Results: [", NROW(walked_data), "]")
attr(walked_data, "dateRange") <- list(startDate = start, endDate = end)
attr(walked_data, "totalResults") <- NROW(walked_data)
attr(walked_data, "samplingLevel") <- "HIGHER_PRECISION, WALKED"
attr(walked_data, "containsSampledData") <- anyBatchSampled
attr(walked_data, "samplePercent") <- samplePercent / length(dates)
walked_data
}
Below is an example for a link shortner API call to goo.gl:
#' Shortens a url using goo.gl
#'
#' @param url URl to shorten with goo.gl
#'
#' @return a string of the short URL
#'
#' Documentation: https://developers.google.com/url-shortener/v1/getting_started
## a wrapper for the function that users pass in the URL to shorten
shorten_url <- function(url){
## turns into {'longUrl' : '<<example.com>>'} when using jsonlite::toJSON(body)
body = list(
longUrl = url
)
## generate the API call function
## POST https://www.googleapis.com/urlshortener/v1/url
## response has 4 objects $kind, $id, $longUrl, and $status, but we only want $id
f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
"POST",
data_parse_function = function(x) x$id)
## now the function has been generated, pass in the body.
## this function has no need for path_arguments or pars_arguments, but that will differ for other APIs.
f(the_body = body)
}
## to use:
gar_auth()
shorten_url("http://www.google.com")
If you want to create a Shiny app just using your data, upload the app with your own .httr-oauth
.
If you want to make a multi-user Shiny app, where users login to their own Google account and the app works with their data, googleAuthR provides these functions to help make the Google login process as easy as possible:
loginOutput()
- creates the client side login button for users to authenticate with.renderLogin()
- creates the server side login button for users to authenticate with.reactiveAccessToken()
- creates the user's authentication token.with_shiny()
- wraps your API functions so they can be passed the user's authentication token.## in global.R
library(googleAuthR)
options("googleAuthR.scopes.selected" = c("https://www.googleapis.com/auth/urlshortener"))
shorten_url <- function(url){
body = list(
longUrl = url
)
f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
"POST",
data_parse_function = function(x) x$id)
f(the_body = body)
}
## in server.R
library(shiny)
library(googleAuthR)
source('global.R')
shinyServer(function(input, output, session){
## Get auth code from return URL
access_token <- reactiveAccessToken(session)
## Make a loginButton to display using loginOutput
output$loginButton <- renderLogin(session, access_token())
short_url_output <- eventReactive(input$submit, {
## wrap existing function with_shiny
## pass the reactive token in shiny_access_token
## pass other named arguments
short_url <- with_shiny(f = shorten_url,
shiny_access_token = access_token(),
url=input$url)
})
output$short_url <- renderText({
short_url_output()
})
})
## in ui.R
library(shiny)
library(googleAuthR)
shinyUI(
fluidPage(
loginOutput("loginButton"),
textInput("url", "Enter URL"),
actionButton("submit", "Shorten URL"),
textOutput("short_url")
))
See more at ?gar_api_generator
once the documentation has caught up.
Below is an example building a link shortner R package using googleAuthR
.
It was done referring to the documentation for Google URL shortener.
Note the help docs specifies the steps outlined above. These are in general the steps for every Google API.
https://www.googleapis.com/urlshortener/v1/url
)POST
library(googleAuthR)
## change the native googleAuthR scopes to the one needed.
options("googleAuthR.scopes.selected" =
c("https://www.googleapis.com/auth/urlshortener"))
#' Shortens a url using goo.gl
#'
#' @param url URl to shorten with goo.gl
#'
#' @return a string of the short URL
shorten_url <- function(url){
body = list(
longUrl = url
)
f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
"POST",
data_parse_function = function(x) x$id)
f(the_body = body)
}
#' Expands a url that has used goo.gl
#'
#' @param shortUrl Url that was shortened with goo.gl
#'
#' @return a string of the expanded URL
expand_url <- function(shortUrl){
f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
"GET",
pars_args = list(shortUrl = "shortUrl"),
data_parse_function = function(x) x)
f(pars_arguments = list(shortUrl = shortUrl))
}
#' Get analyitcs of a url that has used goo.gl
#'
#' @param shortUrl Url that was shortened with goo.gl
#' @param timespan The time period for the analytics data
#'
#' @return a dataframe of the goo.gl Url analytics
analytics_url <- function(shortUrl,
timespan = c("allTime", "month", "week","day","twoHours")){
timespan <- match.arg(timespan)
f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url",
"GET",
pars_args = list(shortUrl = "shortUrl",
projection = "FULL"),
data_parse_function = function(x) {
a <- x$analytics
return(a[timespan][[1]])
})
f(pars_arguments = list(shortUrl = shortUrl))
}
#' Get the history of the authenticated user
#'
#' @return a dataframe of the goo.gl user's history
user_history <- function(){
f <- gar_api_generator("https://www.googleapis.com/urlshortener/v1/url/history",
"GET",
data_parse_function = function(x) x$items)
f()
}
To use the above functions:
library(googleAuthR)
# go through authentication flow
gar_auth()
s <- shorten_url("http://markedmondson.me")
s
expand_url(s)
analytics_url(s, timespan = "month")
user_history()