Skip to content

Commit

Permalink
version 1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark Edmondson authored and cran-robot committed Sep 7, 2019
1 parent d4acbf5 commit 36aca46
Show file tree
Hide file tree
Showing 18 changed files with 521 additions and 421 deletions.
6 changes: 3 additions & 3 deletions DESCRIPTION
@@ -1,6 +1,6 @@
Package: googleAuthR
Type: Package
Version: 1.0.0
Version: 1.1.0
Title: Authenticate and Create Google APIs
Description: Create R functions that interact with OAuth2 Google APIs
<https://developers.google.com/apis-explorer/> easily,
Expand All @@ -26,7 +26,7 @@ LazyData: true
VignetteBuilder: knitr
RoxygenNote: 6.1.1
NeedsCompilation: no
Packaged: 2019-08-31 09:52:21 UTC; mark
Packaged: 2019-09-06 19:01:21 UTC; mark
Author: Mark Edmondson [aut, cre] (<https://orcid.org/0000-0002-8434-3881>),
Jennifer Bryan [ctb],
Johann deBoer [ctb],
Expand All @@ -35,4 +35,4 @@ Author: Mark Edmondson [aut, cre] (<https://orcid.org/0000-0002-8434-3881>),
Joe Cheng [ctb]
Maintainer: Mark Edmondson <m@sunholo.com>
Repository: CRAN
Date/Publication: 2019-08-31 11:00:02 UTC
Date/Publication: 2019-09-07 16:00:02 UTC
34 changes: 17 additions & 17 deletions MD5
@@ -1,11 +1,11 @@
64e90d1c2a540b1f4d08309c729ff15a *DESCRIPTION
273a8ece49e69deb40dccfab9c71e6b4 *DESCRIPTION
1a7dd189ced65f0849efcebc9c2be3ca *LICENSE
dce0906ff8295c367cf4030c74dc171c *NAMESPACE
8e36bf6fb9afe59831fd5dd9005e4844 *NEWS.md
b6b0f26a7306cee55b9a74e4074ac232 *NEWS.md
02ce9c2f7fe00e18d3338263d90ae301 *R/auth.R
f50a936887009f1cf184eeee20815b50 *R/auth_gargle.R
0e0b5f072d3c78b15a9f2f22bfd63ee0 *R/auth_legacy.R
5f68fa161cf94ed514058bfb51b7a846 *R/auto_auth.R
fdb9d2dc51ee85f3765c133c3b3a3baf *R/auto_auth.R
513fcb2187f361dcc820c73b8210ece3 *R/batch.R
207fa7f63414d2d5eecf83128597de93 *R/checks.R
c5f715bf2ff270a98e65b2fff6001b64 *R/debugging.R
Expand All @@ -14,7 +14,7 @@ b4e059e4cc12f7089fd4e8f123c155f0 *R/deprecated.R
bd61a447da3fd696421300f2f1c6b996 *R/discovery_build.R
c2fa4156d94aad9907c5059d0c12e361 *R/discovery_utilities.R
ef129e7f705a87082fdd784fa4a1119f *R/gadget.R
5e5f028bb4aa360b7731ab2c6c8748fb *R/gce.R
5de274d271f04fc5347acc988c1aa43f *R/gce.R
6ccb4c0526a0b65047c6569f038e0834 *R/generator.R
c8e880bcbb8481de8df859434542528e *R/googleAuthR.R
92791bd9b7bf02096023bffeb194e328 *R/memoise.R
Expand All @@ -27,20 +27,20 @@ b1d12e37680ecb0302cd78af0bc83f62 *R/set_client.R
9fdc4474f68ea9ed116cf4189ec5856c *R/shiny-modifyurl.R
d2c60c8168d4cebc7682939a6b58deb3 *R/shiny.R
5840bed076148953b7c4ae41212c9d4b *R/test-helpers.R
54d7879d89e7197e523b5c2a5e8a205d *R/utility.R
b448459b347cf263387f2bb9429b0ea9 *README.md
fbbdd385b5c51e7cf5477967b05da299 *R/utility.R
709889ca54880b7de202ecf04a237a52 *README.md
4a0ea11bde29110ac92cf69eda4e7e77 *build/vignette.rds
ef476c247c435987f52f5859d17e95b7 *inst/css/button.css
04cf39af326fd96857869296636dff20 *inst/doc/advanced-building.Rmd
a8fca704c17cb9d19edd9aa6ffbbc803 *inst/doc/advanced-building.html
be34be5241cb01317d6680fbe63bf1ae *inst/doc/advanced-building.html
e62f0c996ba3720ac0a60c07362f48f6 *inst/doc/building.Rmd
e563a5199efd99446fb38385c0bcaa7d *inst/doc/building.html
12401028c1928628b7ebac4d107fa1f2 *inst/doc/google-authentication-types.Rmd
7c290d60d5e8005fc692c12116a3b3d5 *inst/doc/google-authentication-types.html
57f58c6779eba2463a7d8a13459abb8f *inst/doc/setup.Rmd
ec75101a1eb57053bdc9a8c802d0b666 *inst/doc/setup.html
77db06f7fa91e0337c346f58d8a65186 *inst/doc/building.html
a2127dcf558822609e025857a06f70cc *inst/doc/google-authentication-types.Rmd
ed4eda707f219eda2da16adbf7f57658 *inst/doc/google-authentication-types.html
b989cc3421af86abc305736653cadea7 *inst/doc/setup.Rmd
e70e148051a20cc376d8ab7e7f9f56e6 *inst/doc/setup.html
01e1ed89d746c89389fc91d5b8ba62c2 *inst/doc/troubleshooting.Rmd
7e9526fbe65b93f07262bf66410ef19c *inst/doc/troubleshooting.html
994626d88a861df6747e2edc63ed5d25 *inst/doc/troubleshooting.html
a3f9b806f1ed4baf006d50611078b575 *inst/googledrive_shiny_demo/listGoogleDrive/app.R
477376d302911be898d715e94ca58a88 *inst/img/google_logo.png
cf8bfce980636f2aced78d8e6cddc1ae *inst/js/js-auth.js
Expand All @@ -65,7 +65,7 @@ c067054a2789cb23afa47c98c8e427be *man/gar_attach_auto_auth.Rd
35fd3bb485c7663443350115eaf20583 *man/gar_auth.Rd
17ad96d0eca8e0d1b07fec2999049729 *man/gar_auth_configure.Rd
de289273848088712b8c666e3f9c2cdf *man/gar_auth_service.Rd
abf48bd0ff4d6630621c3f9fdbe28a23 *man/gar_auto_auth.Rd
ed295c12773d1fb6e58662aee5744ae6 *man/gar_auto_auth.Rd
51492cf17288585e5cf5d1949a6b95c1 *man/gar_batch.Rd
d4bde815fd763ccf239ec589bf32ce2b *man/gar_batch_walk.Rd
aa7fe9677f42ea8047be6d7ba39b449c *man/gar_cache_setup.Rd
Expand All @@ -78,7 +78,7 @@ ef03b23e53d86b7809dd1ff55bcbc8de *man/gar_deauth.Rd
8c3e92a694644e36168e6c8c18225562 *man/gar_discovery_api.Rd
e787abe11d7c7a0e5ca729a9fab610dc *man/gar_discovery_apis_list.Rd
f7b720737ef59986929088be331ef922 *man/gar_gce_auth.Rd
d9db780577ae273f0904e65a84f9877d *man/gar_gce_auth_default.Rd
048917d7af08e51d7a005fc70180df9e *man/gar_gce_auth_default.Rd
9cf44d160d7bfd74b1703d3acf8d1465 *man/gar_gce_auth_email.Rd
191ce0f7757ddfa698b031cf1d3abc57 *man/gar_has_token.Rd
0bea35bbe8f14e9a056e3f588490edc2 *man/gar_set_client.Rd
Expand All @@ -103,6 +103,6 @@ c659165619e07515dc629f7a03f9e22e *man/skip_if_no_env_auth.Rd
e7a84853084c59a8723a9b7f85d652e7 *man/with_shiny.Rd
04cf39af326fd96857869296636dff20 *vignettes/advanced-building.Rmd
e62f0c996ba3720ac0a60c07362f48f6 *vignettes/building.Rmd
12401028c1928628b7ebac4d107fa1f2 *vignettes/google-authentication-types.Rmd
57f58c6779eba2463a7d8a13459abb8f *vignettes/setup.Rmd
a2127dcf558822609e025857a06f70cc *vignettes/google-authentication-types.Rmd
b989cc3421af86abc305736653cadea7 *vignettes/setup.Rmd
01e1ed89d746c89389fc91d5b8ba62c2 *vignettes/troubleshooting.Rmd
6 changes: 6 additions & 0 deletions NEWS.md
@@ -1,3 +1,9 @@
# googleAuthR v1.1.0

* Add default scope of "https://www.googleapis.com/auth/cloud-platform" to `gar_gce_auth()`
* Improvements to stop `gar_auto_auth()` block library installation if auth files not correct.
* Allow to be used by R 3.3 via custom `isFALSE` function (#158 - thanks @matthijsvanderloos)

# googleAuthR v1.0.0

* Implement `library(gargle)` as backend for authentication functions
Expand Down
23 changes: 16 additions & 7 deletions R/auto_auth.R
Expand Up @@ -29,14 +29,18 @@
#' @import assertthat
#' @importFrom tools file_ext
gar_auto_auth <- function(required_scopes,
no_auto = FALSE,
no_auto = NULL,
environment_var = "GAR_AUTH_FILE",
new_user = NULL) {

if(!is.null(new_user)){
warning("Argument new_user is deprecated and will be removed next release.")
}

if(!is.null(no_auto)){
warning("Argument no_auto is deprecated and will be removed next release.")
}

if(is.null(required_scopes)){
myMessage("No scopes have been set, set them via
options(googleAuthR.scopes.selected)
Expand All @@ -55,20 +59,25 @@ gar_auto_auth <- function(required_scopes,
paste(getOption("googleAuthR.scopes.selected"), collapse = " "))
}

if(no_auto){
return(gar_auth())
}


auth_file <- Sys.getenv(environment_var)

if(auth_file == ""){
## normal auth looking for .httr-oauth in working folder or new user
return(gar_auth())
## Can't do anything, return.
return(NULL)
}

if(grepl("^[[:alnum:].-_]+@[[:alnum:].-]+$", auth_file)){
myMessage("Auto-auth - email address", level = 2)
return(gar_auth(email = auth_file))

token <- gargle::credentials_user_oauth2(
scopes = getOption("googleAuthR.scopes.selected"),
app = gar_oauth_app(),
package = "googleAuthR",
email = auth_file
)
return(token)
}

if(!file.exists(auth_file)){
Expand Down
3 changes: 2 additions & 1 deletion R/gce.R
Expand Up @@ -37,7 +37,8 @@
#' @export
#' @importFrom gargle credentials_app_default
#' @seealso \href{https://cloud.google.com/sdk/gcloud/reference/auth/application-default/print-access-token}{gcloud reference}
gar_gce_auth_default <- function(scopes = getOption("googleAuthR.scopes.selected")){
gar_gce_auth_default <- function(scopes = getOption("googleAuthR.scopes.selected",
"https://www.googleapis.com/auth/cloud-platform")){

# doesn't this need the returned access token?
credentials_app_default(scopes = scopes)
Expand Down
2 changes: 2 additions & 0 deletions R/utility.R
@@ -1,3 +1,5 @@
isFALSE <- function(x) identical(x, FALSE) # replicate R 3.5 function

#' @noRd
#' @param filename location of JavaScript file with %s template locations in this package's inst folder
#' @param ... The correct number of strings to be replaced into %s's locations of filename
Expand Down
15 changes: 15 additions & 0 deletions README.md
Expand Up @@ -4,6 +4,21 @@
[![Travis-CI Build Status](https://travis-ci.org/MarkEdmondson1234/googleAuthR.svg?branch=master)](https://travis-ci.org/MarkEdmondson1234/googleAuthR)
[![codecov](https://codecov.io/gh/MarkEdmondson1234/googleAuthR/branch/master/graph/badge.svg)](https://codecov.io/gh/MarkEdmondson1234/googleAuthR)

## gargle backend

As of version `googleAuthR>=1.0.0` the OAuth2 and service JSON authentication is provided by [gargle](https://gargle.r-lib.org/index.html). Refer to that documentation for details.

The plan is to migrate as much functionality to `gargle` from `googleAuthR`, but backward compatibility will be maintained for all packages depending on `googleAuthR` in the meantime.

Once there is feature parity, client packages can then migrate totally to `gargle`. At time of writing some of the major features not in `gargle` yet are:

* Shiny authentication flows
* Paging
* Caching
* Batching

If you are not using the above then you can use `gargle` directly now. Otherwise you can still use `googleAuthR` that will use the features of `gargle` and wait for more features to be migrated.

## Overview

This library allows you to authenticate easily via local use in an OAuth2 flow; within a Shiny app; or via service accounts.
Expand Down
6 changes: 3 additions & 3 deletions inst/doc/advanced-building.html
Expand Up @@ -13,7 +13,7 @@

<meta name="author" content="Mark Edmondson" />

<meta name="date" content="2019-08-31" />
<meta name="date" content="2019-09-06" />

<title>Advanced Google API building techniques</title>

Expand Down Expand Up @@ -304,7 +304,7 @@

<h1 class="title toc-ignore">Advanced Google API building techniques</h1>
<h4 class="author">Mark Edmondson</h4>
<h4 class="date">2019-08-31</h4>
<h4 class="date">2019-09-06</h4>



Expand Down Expand Up @@ -605,7 +605,7 @@ <h3>Using caching</h3>
<h2>Tests</h2>
<p>All packages should ideally have tests to ensure that any changes do not break functionality.</p>
<p>If new to testing, first read this guide on using the <code>tidyverse</code>’s <a href="http://testthat.r-lib.org/">testthat</a>, then read this for a <a href="https://juliasilge.com/blog/beginners-guide-to-travis/">beginner’s guide to travis-CI for R</a>, which is a continuous integration system that will run and test your code everytime it is committed to GitHub.</p>
<p>This is this package’s current Travis badge: <a href="https://travis-ci.org/MarkEdmondson1234/googleAuthR"><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3NiIgaGVpZ2h0PSIyMCI+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4Mj0iMCIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2JiYiIgc3RvcC1vcGFjaXR5PSIuMSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIuMSIvPjwvbGluZWFyR3JhZGllbnQ+PHJlY3Qgcng9IjMiIHdpZHRoPSI3NiIgaGVpZ2h0PSIyMCIgZmlsbD0iIzU1NSIvPjxyZWN0IHJ4PSIzIiB4PSIzNyIgd2lkdGg9IjM5IiBoZWlnaHQ9IjIwIiBmaWxsPSIjOWY5ZjlmIi8+PHBhdGggZmlsbD0iIzlmOWY5ZiIgZD0iTTM3IDBoNHYyMGgtNHoiLz48cmVjdCByeD0iMyIgd2lkdGg9Ijc2IiBoZWlnaHQ9IjIwIiBmaWxsPSJ1cmwoI2EpIi8+PGcgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IkRlamFWdSBTYW5zLFZlcmRhbmEsR2VuZXZhLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiPjx0ZXh0IHg9IjE5LjUiIHk9IjE1IiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIj5idWlsZDwvdGV4dD48dGV4dCB4PSIxOS41IiB5PSIxNCI+YnVpbGQ8L3RleHQ+PHRleHQgeD0iNTUuNSIgeT0iMTUiIGZpbGw9IiMwMTAxMDEiIGZpbGwtb3BhY2l0eT0iLjMiPmVycm9yPC90ZXh0Pjx0ZXh0IHg9IjU1LjUiIHk9IjE0Ij5lcnJvcjwvdGV4dD48L2c+PC9zdmc+" alt="Travis-CI Build Status" /></a></p>
<p>This is this package’s current Travis badge: <a href="https://travis-ci.org/MarkEdmondson1234/googleAuthR"><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MCIgaGVpZ2h0PSIyMCI+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4Mj0iMCIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2JiYiIgc3RvcC1vcGFjaXR5PSIuMSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIuMSIvPjwvbGluZWFyR3JhZGllbnQ+PHJlY3Qgcng9IjMiIHdpZHRoPSI5MCIgaGVpZ2h0PSIyMCIgZmlsbD0iIzU1NSIvPjxyZWN0IHJ4PSIzIiB4PSIzNyIgd2lkdGg9IjUzIiBoZWlnaHQ9IjIwIiBmaWxsPSIjNGMxIi8+PHBhdGggZmlsbD0iIzRjMSIgZD0iTTM3IDBoNHYyMGgtNHoiLz48cmVjdCByeD0iMyIgd2lkdGg9IjkwIiBoZWlnaHQ9IjIwIiBmaWxsPSJ1cmwoI2EpIi8+PGcgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IkRlamFWdSBTYW5zLFZlcmRhbmEsR2VuZXZhLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiPjx0ZXh0IHg9IjE5LjUiIHk9IjE1IiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIj5idWlsZDwvdGV4dD48dGV4dCB4PSIxOS41IiB5PSIxNCI+YnVpbGQ8L3RleHQ+PHRleHQgeD0iNjIuNSIgeT0iMTUiIGZpbGw9IiMwMTAxMDEiIGZpbGwtb3BhY2l0eT0iLjMiPnBhc3Npbmc8L3RleHQ+PHRleHQgeD0iNjIuNSIgeT0iMTQiPnBhc3Npbmc8L3RleHQ+PC9nPjwvc3ZnPg==" alt="Travis-CI Build Status" /></a></p>
<p>However, testing APIs that need authentication is more complicated, as you need to deal with the authentication token to get a correct response.</p>
<p>One option is to encrypt and upload your token, for which you can read a guide by Jenny Bryan here - <code>https://cran.r-project.org/web/packages/googlesheets/vignettes/managing-auth-tokens.html</code>.</p>
<div id="mocking-tests-with-httptest" class="section level3">
Expand Down
4 changes: 2 additions & 2 deletions inst/doc/building.html
Expand Up @@ -13,7 +13,7 @@

<meta name="author" content="Mark Edmondson" />

<meta name="date" content="2019-08-31" />
<meta name="date" content="2019-09-06" />

<title>Building your own Google R library</title>

Expand Down Expand Up @@ -304,7 +304,7 @@

<h1 class="title toc-ignore">Building your own Google R library</h1>
<h4 class="author">Mark Edmondson</h4>
<h4 class="date">2019-08-31</h4>
<h4 class="date">2019-09-06</h4>



Expand Down
56 changes: 44 additions & 12 deletions inst/doc/google-authentication-types.Rmd
Expand Up @@ -9,21 +9,31 @@ vignette: >
%\VignetteEncoding{UTF-8}
---

# Basic
## Quick user based authentication

Once setup, then you should go through the Google login flow in your browser when you run this command:

```r
googleAuthR::gar_auth()
library(googleAuthR)
# starts auth process with defaults
gar_auth()
#>The googleAuthR package is requesting access to your Google account. Select a
#> pre-authorised account or enter '0' to obtain a new token. Press Esc/Ctrl + C to abort.

#> 1: mark@work.com
#> 2: home@home.com
```

If you ever need to authenticate with a new user, use:
The authentication cache token is kept at a global level as per the `gargle` library documentation - [see there for more details](https://gargle.r-lib.org/).

You can also specify your email to avoid the interactive menu:

```r
googleAuthR::gar_auth(new_user=TRUE)
gar_auth(email = "your@email.com")
```

Authentication token is by default cached in a hidden file called `.httr-oauth` in the working directory.
These functions are usually wrapped in package specific functions when used in other packages, such as `googleAnalyticsR::ga_auth()`


## Client options

Expand All @@ -39,6 +49,29 @@ If creating your own library you can choose to supply some or all of the above t

## Multiple authentication tokens

### googleAuthR > 1.0.0

Authentication cache tokens are kept at a global level on your computer. When you authenticate the first time with a new client.id, scope or email then you will go through the authentication process in the browser, however the next time it wil be cached and be a lot quicker.

```r
# switching between auth scopes
# first time new scope manual auth, then auto if supplied email
gar_auth(email = "your@email.com",
scopes = "https://www.googleapis.com/auth/drive")

# ... query Google Drive functions ...

gar_auth(email = "your@email.com",
scopes = "https://www.googleapis.com/auth/bigquery")

# ..query BigQuery functions ...

```

### Legacy flows

> Applicable before `googleAuthR < 1.0.0`
If you supply a filename to `googleAuthR::gar_auth(token = "filename")` then it will save the token there. If it doesn't exist, it will make a new one, if it does exist it will attempt to read the token from that file. Relative and absolute filenames work.

You can use different token names to save different authentication settings such as with different scopes and client Ids.
Expand Down Expand Up @@ -92,7 +125,7 @@ list_websites()

# Setting the client via Google Cloud client JSON

To avoid keeping track of which client_id/secret to use, Google offers a client ID JSON file you can download from the Google Cloud console here - `https://console.cloud.google.com/apis/credentials`. Make sure the client ID type is `Other`.
To avoid keeping track of which client_id/secret to use, Google offers a client ID JSON file you can download from the Google Cloud console here - `https://console.cloud.google.com/apis/credentials`. Make sure the client ID type is `Other` for desktop applications.

You can use this to set the client details before your first authentication. The above example would then be:

Expand All @@ -108,8 +141,8 @@ scopes = c("https://www.googleapis.com/auth/analytics",
# set the client
gar_set_client("client-id.json", scopes = scopes)

# authenticate and go through the OAuth2 flow first time - specify a filename to save to by passing it in
gar_auth(token = "sc_ga.httr-oauth")
# authenticate and go through the OAuth2 flow first time
gar_auth()

# can run Google Analytics API calls:
ga_account_list()
Expand All @@ -133,8 +166,7 @@ gar_set_client(scopes = "https://www.googleapis.com/auth/webmasters")

# Authentication with no browser

If for some reason you need authentication without access to a browser (for example when using RStudio Server), then you can authenticate locally and upload the `.httr-oauth` file to the folder of your script.
Authentication via `gar_auth()` when on a server will default to `oob` style where you will need to copy-paste the token rather than R return the approval token for you (but you will need to allow popups)
Refer to [this gargle article](https://gargle.r-lib.org/articles/non-interactive-auth.html) on how to authenticate in a non-interactive manner

# Authentication with a JSON file via Service Accounts

Expand Down Expand Up @@ -194,7 +226,7 @@ list_websites()

# Authentication within Shiny

If you want to create a Shiny app just using your data, upload the app with your own `.httr-oauth`.
If you want to create a Shiny app just using your data, refer to the [non-interactive authentication article on gargle](https://gargle.r-lib.org/articles/non-interactive-auth.html)

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 the below functions to help make the Google login process as easy as possible.

Expand Down Expand Up @@ -552,7 +584,7 @@ GCS_AUTH_FILE="/Users/mark/auth/my_auth_file.json"

# Revoking Authentication

For local use, delete the `.httr-oauth` file.
For local use, call `gar_deauth()` to unauthentiucate a session. To avoid cache tokens being reused delete them from the gargle cache folder, usually `~/.R/gargle/gargle-oauth/`

For service level accounts delete the JSON file.

Expand Down

0 comments on commit 36aca46

Please sign in to comment.