├── .Rbuildignore ├── .gitignore ├── CONDUCT.md ├── DESCRIPTION ├── LICENSE ├── NAMESPACE ├── NEWS.md ├── R ├── api.R ├── auth.R ├── lists.R ├── sites.R └── utils-pipe.R ├── README.Rmd ├── README.md ├── man ├── get_list_entries.Rd ├── get_lists.Rd ├── get_sites.Rd ├── msgraph_api.Rd ├── msgraph_auth.Rd ├── msgraph_token.Rd └── pipe.Rd └── msgraphr.Rproj /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^CONDUCT\.md$ 4 | ^README\.Rmd$ 5 | ^README-.*\.png$ 6 | ^\.httr-oauth$ 7 | ^\.msgraphr_token\.rds$ 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .msgraphr_token.rds 5 | -------------------------------------------------------------------------------- /CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who 4 | contribute through reporting issues, posting feature requests, updating documentation, 5 | submitting pull requests or patches, and other activities. 6 | 7 | We are committed to making participation in this project a harassment-free experience for 8 | everyone, regardless of level of experience, gender, gender identity and expression, 9 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. 10 | 11 | Examples of unacceptable behavior by participants include the use of sexual language or 12 | imagery, derogatory comments or personal attacks, trolling, public or private harassment, 13 | insults, or other unprofessional conduct. 14 | 15 | Project maintainers have the right and responsibility to remove, edit, or reject comments, 16 | commits, code, wiki edits, issues, and other contributions that are not aligned to this 17 | Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed 18 | from the project team. 19 | 20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by 21 | opening an issue or contacting one or more of the project maintainers. 22 | 23 | This Code of Conduct is adapted from the Contributor Covenant 24 | (http:contributor-covenant.org), version 1.0.0, available at 25 | http://contributor-covenant.org/version/1/0/0/ 26 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: msgraphr 2 | Title: Light weight interface for liberating SharePoint Online list data via the MS Graph API 3 | Version: 0.0.0.9000 4 | Authors@R: utils::person("David", "Severski", email = "davidski@deadheaven.com", role = c("cre", "aut"), comment = "= 3.5.0) 10 | License: MIT + file LICENSE 11 | Encoding: UTF-8 12 | LazyData: true 13 | RoxygenNote: 7.1.0 14 | Imports: 15 | magrittr, 16 | httr, 17 | jsonlite, 18 | dplyr, 19 | glue, 20 | janitor, 21 | rlang 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2018 2 | COPYRIGHT HOLDER: Your name goes here 3 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export("%>%") 4 | export(get_list_entries) 5 | export(get_lists) 6 | export(get_sites) 7 | export(msgraph_api) 8 | export(msgraph_auth) 9 | importFrom(dplyr,select) 10 | importFrom(glue,glue) 11 | importFrom(httr,GET) 12 | importFrom(httr,accept_json) 13 | importFrom(httr,authenticate) 14 | importFrom(httr,content) 15 | importFrom(httr,http_type) 16 | importFrom(httr,modify_url) 17 | importFrom(httr,oauth2.0_token) 18 | importFrom(httr,oauth_app) 19 | importFrom(httr,oauth_endpoint) 20 | importFrom(janitor,clean_names) 21 | importFrom(jsonlite,fromJSON) 22 | importFrom(magrittr,"%>%") 23 | importFrom(rlang,.data) 24 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # msgraphr 0.0.0.9000 2 | 3 | * Added a `NEWS.md` file to track changes to the package. 4 | -------------------------------------------------------------------------------- /R/api.R: -------------------------------------------------------------------------------- 1 | default_api_version <- function() {"v1.0"} # default to the current production version 2 | 3 | msgraph_url <- function(api_version = "v1.0") { 4 | paste0("https://graph.microsoft.com/", api_version) 5 | } 6 | 7 | #' Find the location of the OAuth token cache 8 | #' 9 | #' @return Path to the on-disk OAuth cache 10 | msgraph_token <- function() { 11 | token <- Sys.getenv('MSGRAPH_PAT', "") 12 | if (token == "") { 13 | token <- ".msgraphr_token.rds" 14 | warning("Defaulting to the current working directory for saving the OAuth token. Set the MSGRAPH_PAT envirornment variable to a file location to override.", .call = FALSE) 15 | } 16 | token 17 | } 18 | 19 | msgraph_read_token <- function(token_location = msgraph_token()) { 20 | if (!file.exists(token_location)) stop("Unable to find the MSGRAPH OAuth token on disk.", .call = FALSE) 21 | readRDS(token_location) 22 | } 23 | 24 | #' Execute a GET request against the MS Graph API 25 | #' 26 | #' Calls bare API end points and returns a wrapped `httr` response object 27 | #' to a higher level function for presentation. 28 | #' 29 | #' @param path API endpoint to retrieve 30 | #' @param msgraph_token Oauth2 token as returned from `httr::oauth2_token()` 31 | #' @param query Named list of parameters to use as the URL query 32 | #' @param api_version Version of the Graph API to call 33 | #' 34 | #' @return A msgraph_api object 35 | #' @export 36 | #' 37 | #' @importFrom httr modify_url GET http_type accept_json authenticate content 38 | #' @importFrom jsonlite fromJSON 39 | #' 40 | #' @examples 41 | #' NULL 42 | msgraph_api <- function(path, msgraph_token, query = NULL, api_version = default_api_version()) { 43 | path <- c(api_version, path) 44 | url <- httr::modify_url(msgraph_url(), path = path, query = query) 45 | resp <- httr::GET(url, httr::accept_json(), config = httr::config(token = msgraph_token)) 46 | if (httr::http_type(resp) != "application/json") { 47 | stop("API did not return json", call. = FALSE) 48 | } 49 | 50 | parsed <- jsonlite::fromJSON(httr::content(resp, "text"), simplifyVector = TRUE) 51 | 52 | structure( 53 | list( 54 | content = parsed, 55 | path = paste0(path, collaspe = "/"), 56 | response = resp 57 | ), 58 | class = "msgraph_api" 59 | ) 60 | } 61 | 62 | print.msgraph_api <- function(x, ...) { 63 | cat("\n", sep = "") 64 | utils::str(x$content) 65 | invisible(x) 66 | } 67 | -------------------------------------------------------------------------------- /R/auth.R: -------------------------------------------------------------------------------- 1 | #' Perform the OAuth 2.0 dance with MS Graph 2 | #' 3 | #' @return A `httr` oauth token object 4 | #' 5 | #' @param client_id Application Id from MS Graph 6 | #' @param client_secret Application password from MS Graph 7 | #' @export 8 | #' @importFrom httr oauth_endpoint oauth2.0_token oauth_app 9 | #' 10 | #' @examples 11 | #' NULL 12 | msgraph_auth <- function(client_id, client_secret) { 13 | app_name <- 'Rstats Queries' # not important for authorization grant flow 14 | 15 | # API resource ID to request access for, e.g. MS Graph 16 | resource_uri <- 'https://graph.microsoft.com' 17 | 18 | # Scopes to authorize 19 | scopes <- c("User.Read", "Sites.Read.All", "offline_access") 20 | 21 | msgraph_endpoint <- httr::oauth_endpoint(base_url = "https://login.microsoftonline.com/consumers", 22 | authorize = "oauth2/v2.0/authorize", 23 | access = "oauth2/v2.0/token") 24 | 25 | # Create the app instance 26 | myapp <- httr::oauth_app(appname = app_name, 27 | key = client_id, 28 | secret = client_secret) 29 | 30 | msgraph_token <- httr::oauth2.0_token(msgraph_endpoint, myapp, 31 | scope = scopes, 32 | cache = msgraph_token(), 33 | #user_params = list(resource = resource_uri), 34 | use_oob = FALSE) 35 | if (('error' %in% names(msgraph_token$credentials)) && 36 | (nchar(msgraph_token$credentials$error) > 0)) { 37 | errorMsg <- paste( 38 | 'Error while acquiring token.', 39 | paste('Error message:', msgraph_token$credentials$error), 40 | paste( 41 | 'Error description:', 42 | msgraph_token$credentials$error_description 43 | ), 44 | paste('Error code:', msgraph_token$credentials$error_codes), 45 | sep = '\n' 46 | ) 47 | stop(errorMsg) 48 | } 49 | 50 | # Resource API can be accessed through "msgraph_token" at this point. 51 | msgraph_token 52 | } 53 | -------------------------------------------------------------------------------- /R/lists.R: -------------------------------------------------------------------------------- 1 | #' Fetch all the lists on a SharePoint site 2 | #' 3 | #' @param site_id Site ID 4 | #' @param token httr auth token 5 | #' 6 | #' @return A somewhat messy list structure :( 7 | #' @export 8 | #' 9 | #' @importFrom glue glue 10 | #' 11 | #' @examples 12 | #' NULL 13 | get_lists <- function(site_id, token) { 14 | msgraph_api(glue::glue("sites/{site_id}/lists"), token) -> all_lists 15 | all_lists$content$value 16 | } 17 | 18 | #' Fetch the items in a given list 19 | #' 20 | #' @param site_id Site ID from `get_sites()` 21 | #' @param list_id List ID from `get_lists()` 22 | #' @param token httr oauth token 23 | #' 24 | #' @return A tibble 25 | #' @export 26 | #' 27 | #' @importFrom janitor clean_names 28 | #' @importFrom glue glue 29 | #' 30 | #' @examples 31 | #' NULL 32 | get_list_entries <- function(site_id, list_id, token) { 33 | msgraph_api(glue::glue("sites/{site_id}/lists/{list_id}/items"), query = list("expand" = "fields"), token) -> list_items 34 | list_items$content$value$fields %>% 35 | janitor::clean_names() -> list_entries 36 | names(list_entries) <- names(list_entries) %>% 37 | gsub(pattern = "_x0020", replacement = "", fixed = TRUE) 38 | list_entries 39 | } 40 | -------------------------------------------------------------------------------- /R/sites.R: -------------------------------------------------------------------------------- 1 | #' Fetch a tibble of SharePoint sites 2 | #' 3 | #' @param searchquery Optional search 4 | #' @param token httr oauth token 5 | #' 6 | #' @return A tibble 7 | #' @export 8 | #' 9 | #' @importFrom dplyr select 10 | #' @importFrom rlang .data 11 | #' 12 | #' @examples 13 | #' NULL 14 | get_sites <- function(searchquery = NULL, token = msgraph_read_token()) { 15 | if (!is.null(searchquery)) searchquery <- list("search" = searchquery) 16 | site_resp <- msgraph_api("sites", query = searchquery, token) 17 | site_resp$content$value %>% dplyr::select(-.data$root) 18 | } 19 | -------------------------------------------------------------------------------- /R/utils-pipe.R: -------------------------------------------------------------------------------- 1 | #' Pipe operator 2 | #' 3 | #' See \code{magrittr::\link[magrittr]{\%>\%}} for details. 4 | #' 5 | #' @name %>% 6 | #' @rdname pipe 7 | #' @keywords internal 8 | #' @export 9 | #' @importFrom magrittr %>% 10 | #' @usage lhs \%>\% rhs 11 | NULL 12 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: 3 | md_document: 4 | variant: markdown_github 5 | --- 6 | 7 | 8 | 9 | ```{r, echo = FALSE} 10 | knitr::opts_chunk$set( 11 | collapse = TRUE, 12 | comment = "#>", 13 | fig.path = "README-" 14 | ) 15 | ``` 16 | 17 | # msgraphr 18 | 19 | > NOTE: This package is quite raw. I am not currently using Office 365 20 | and cannot develop or debug features apart from the general 21 | MS Graph interface. Pull Requests are **most** welcome! 22 | 23 | **msgraphr** is a minimal R wrapper of the SharePoint Online (Office 365) 24 | APIs, aiming to allow R users to pull down list information as dataframes for 25 | local analysis. Liberate your data from SharePoint! 26 | 27 | ## Installation 28 | 29 | 30 | `devtools::install_github("davidski/msgraphr")` 31 | 32 | ## Authentication 33 | 34 | * Create an application at https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade with the following fields: 35 | + `Name` Name of your MS Graph app e.g. `msgraphr_query`. 36 | + Under `Redirect URIs` at a web link pointing to `http://localhost:1410`. 37 | + Take note of the `Application Id`, this will be your `client_id`. 38 | + Under `Certificates & Secrets` generate an application secret and note the 39 | value for use as the `Client Secret`. 40 | + Add a Platform and select `Web`. 41 | + `Allow Implicit Flow`: Unchecked 42 | + Under API Permissions, alter the Microsoft Graph permissions to 43 | specify the scopes for which you wish to grant access. 44 | 45 | * (OPTIONAL, BUT RECOMMENDED) Set the `MSGRAPH_PAT` environment variable to a 46 | file location on disk for `msgraphr` to cache the Oauth2.0 token. Defaults 47 | to the current working directory. 48 | 49 | * To perform the initial authentication. 50 | ``` 51 | msgraph_auth(client_id = "REDACTED", 52 | client_secret = "REDACTED" ) 53 | ``` 54 | 55 | ### Scope used 56 | 57 | * `msgraphr` requests the following access scopes: 58 | + `User` and `Sites.Read.All` for SharePoint online read access 59 | + `offline_user` so that tokens may be auto-refreshed without further user action 60 | 61 | ## Usage 62 | 63 | ```{r example, eval = FALSE} 64 | library(tidyverse) 65 | library(msgraphr) 66 | 67 | # auth 68 | token <- msgraph_auth() 69 | 70 | # list all the sites to which you have accesss 71 | get_sites(search = "", token) %>% head(1) %>% pull(id) -> my_site 72 | 73 | # list all the lists on that site 74 | get_lists(site_id = my_site, token) %>% head(1) %>% pull(id) -> my_list 75 | 76 | # fetch all the entries on that list 77 | get_list_entries(site_id = my_site, list_id = my_list, token) 78 | 79 | # go forth and do amazing things! 80 | ``` 81 | 82 | ## References 83 | 84 | See the [MS Graph](https://developer.microsoft.com/en-us/graph/docs/concepts/overview) API documentation. 85 | 86 | ## Contributing 87 | 88 | Please note that this project is released with a [Contributor Code of Conduct](CONDUCT.md). By participating in this project you agree to abide by its terms. 89 | 90 | ## License 91 | 92 | The [MIT License](LICENSE) applies. 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | msgraphr 4 | ======== 5 | 6 | > NOTE: This package is quite raw. I am not currently using Office 365 7 | > and cannot develop or debug features apart from the general MS Graph 8 | > interface. Pull Requests are **most** welcome! 9 | 10 | **msgraphr** is a minimal R wrapper of the SharePoint Online (Office 11 | 365) APIs, aiming to allow R users to pull down list information as 12 | dataframes for local analysis. Liberate your data from SharePoint! 13 | 14 | Installation 15 | ------------ 16 | 17 | `devtools::install_github("davidski/msgraphr")` 18 | 19 | Authentication 20 | -------------- 21 | 22 | - Create an application at 23 | https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade 24 | with the following fields: 25 | 26 | - `Name` Name of your MS Graph app e.g. `msgraphr_query`. 27 | 28 | - Under `Redirect URIs` at a web link pointing to 29 | `http://localhost:1410`. 30 | 31 | - Take note of the `Application Id`, this will be your `client_id`. 32 | 33 | - Under `Certificates & Secrets` generate an application secret and 34 | note the value for use as the `Client Secret`. 35 | 36 | - Add a Platform and select `Web`. 37 | 38 | - `Allow Implicit Flow`: Unchecked 39 | 40 | - Under API Permissions, alter the Microsoft Graph permissions to 41 | specify the scopes for which you wish to grant access. 42 | 43 | - (OPTIONAL, BUT RECOMMENDED) Set the `MSGRAPH_PAT` environment 44 | variable to a file location on disk for `msgraphr` to cache the 45 | Oauth2.0 token. Defaults to the current working directory. 46 | 47 | - To perform the initial authentication. 48 | 49 | msgraph_auth(client_id = "REDACTED", 50 | client_secret = "REDACTED" ) 51 | 52 | ### Scope used 53 | 54 | - `msgraphr` requests the following access scopes: 55 | - `User` and `Sites.Read.All` for SharePoint online read access 56 | - `offline_user` so that tokens may be auto-refreshed without 57 | further user action 58 | 59 | Usage 60 | ----- 61 | 62 | ``` r 63 | library(tidyverse) 64 | library(msgraphr) 65 | 66 | # auth 67 | token <- msgraph_auth() 68 | 69 | # list all the sites to which you have accesss 70 | get_sites(search = "", token) %>% head(1) %>% pull(id) -> my_site 71 | 72 | # list all the lists on that site 73 | get_lists(site_id = my_site, token) %>% head(1) %>% pull(id) -> my_list 74 | 75 | # fetch all the entries on that list 76 | get_list_entries(site_id = my_site, list_id = my_list, token) 77 | 78 | # go forth and do amazing things! 79 | ``` 80 | 81 | References 82 | ---------- 83 | 84 | See the [MS 85 | Graph](https://developer.microsoft.com/en-us/graph/docs/concepts/overview) 86 | API documentation. 87 | 88 | Contributing 89 | ------------ 90 | 91 | Please note that this project is released with a [Contributor Code of 92 | Conduct](CONDUCT.md). By participating in this project you agree to 93 | abide by its terms. 94 | 95 | License 96 | ------- 97 | 98 | The [MIT License](LICENSE) applies. 99 | -------------------------------------------------------------------------------- /man/get_list_entries.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/lists.R 3 | \name{get_list_entries} 4 | \alias{get_list_entries} 5 | \title{Fetch the items in a given list} 6 | \usage{ 7 | get_list_entries(site_id, list_id, token) 8 | } 9 | \arguments{ 10 | \item{site_id}{Site ID from `get_sites()`} 11 | 12 | \item{list_id}{List ID from `get_lists()`} 13 | 14 | \item{token}{httr oauth token} 15 | } 16 | \value{ 17 | A tibble 18 | } 19 | \description{ 20 | Fetch the items in a given list 21 | } 22 | \examples{ 23 | NULL 24 | } 25 | -------------------------------------------------------------------------------- /man/get_lists.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/lists.R 3 | \name{get_lists} 4 | \alias{get_lists} 5 | \title{Fetch all the lists on a SharePoint site} 6 | \usage{ 7 | get_lists(site_id, token) 8 | } 9 | \arguments{ 10 | \item{site_id}{Site ID} 11 | 12 | \item{token}{httr auth token} 13 | } 14 | \value{ 15 | A somewhat messy list structure :( 16 | } 17 | \description{ 18 | Fetch all the lists on a SharePoint site 19 | } 20 | \examples{ 21 | NULL 22 | } 23 | -------------------------------------------------------------------------------- /man/get_sites.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sites.R 3 | \name{get_sites} 4 | \alias{get_sites} 5 | \title{Fetch a tibble of SharePoint sites} 6 | \usage{ 7 | get_sites(searchquery = NULL, token = msgraph_read_token()) 8 | } 9 | \arguments{ 10 | \item{searchquery}{Optional search} 11 | 12 | \item{token}{httr oauth token} 13 | } 14 | \value{ 15 | A tibble 16 | } 17 | \description{ 18 | Fetch a tibble of SharePoint sites 19 | } 20 | \examples{ 21 | NULL 22 | } 23 | -------------------------------------------------------------------------------- /man/msgraph_api.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/api.R 3 | \name{msgraph_api} 4 | \alias{msgraph_api} 5 | \title{Execute a GET request against the MS Graph API} 6 | \usage{ 7 | msgraph_api( 8 | path, 9 | msgraph_token, 10 | query = NULL, 11 | api_version = default_api_version() 12 | ) 13 | } 14 | \arguments{ 15 | \item{path}{API endpoint to retrieve} 16 | 17 | \item{msgraph_token}{Oauth2 token as returned from `httr::oauth2_token()`} 18 | 19 | \item{query}{Named list of parameters to use as the URL query} 20 | 21 | \item{api_version}{Version of the Graph API to call} 22 | } 23 | \value{ 24 | A msgraph_api object 25 | } 26 | \description{ 27 | Calls bare API end points and returns a wrapped `httr` response object 28 | to a higher level function for presentation. 29 | } 30 | \examples{ 31 | NULL 32 | } 33 | -------------------------------------------------------------------------------- /man/msgraph_auth.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/auth.R 3 | \name{msgraph_auth} 4 | \alias{msgraph_auth} 5 | \title{Perform the OAuth 2.0 dance with MS Graph} 6 | \usage{ 7 | msgraph_auth(client_id, client_secret) 8 | } 9 | \arguments{ 10 | \item{client_id}{Application Id from MS Graph} 11 | 12 | \item{client_secret}{Application password from MS Graph} 13 | } 14 | \value{ 15 | A `httr` oauth token object 16 | } 17 | \description{ 18 | Perform the OAuth 2.0 dance with MS Graph 19 | } 20 | \examples{ 21 | NULL 22 | } 23 | -------------------------------------------------------------------------------- /man/msgraph_token.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/api.R 3 | \name{msgraph_token} 4 | \alias{msgraph_token} 5 | \title{Find the location of the OAuth token cache} 6 | \usage{ 7 | msgraph_token() 8 | } 9 | \value{ 10 | Path to the on-disk OAuth cache 11 | } 12 | \description{ 13 | Find the location of the OAuth token cache 14 | } 15 | -------------------------------------------------------------------------------- /man/pipe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils-pipe.R 3 | \name{\%>\%} 4 | \alias{\%>\%} 5 | \title{Pipe operator} 6 | \usage{ 7 | lhs \%>\% rhs 8 | } 9 | \description{ 10 | See \code{magrittr::\link[magrittr]{\%>\%}} for details. 11 | } 12 | \keyword{internal} 13 | -------------------------------------------------------------------------------- /msgraphr.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | --------------------------------------------------------------------------------