Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial paginate function #650

Merged
merged 19 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions docs/paginators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Paginators

> Some AWS operations return results that are incomplete and require subsequent requests in order to attain the entire result set. The process of sending subsequent requests to continue where a previous request left off is called pagination. For example, the list_objects operation of Amazon S3 returns up to 1000 objects at a time, and you must send subsequent requests with the appropriate Marker in order to retrieve the next page of results.
(https://boto3.amazonaws.com/v1/documentation/api/latest/guide/paginators.html#paginators)


As of `paws v0.4.0+` paginators are supported within `paws`.

## Basic Usage

A paginator can be applied to a `paws` operation. `paws` support 3 different methods of paginator (`paginate`, `paginate_lapply`, `paginate_sapply`).

### `paginate`:
Return all response from the `paws` operation.

```r
library(paws)

svc <- s3(region = "us-west-2")

results <- paginate(svc$list_objects(Bucket = "my-bucket"))
```

### `paginate_lapply`:
Allows you to apply a function on each returning response.
```r
library(paws)

svc <- s3(region = "us-west-2")

results <- paginate_lapply(svc$list_objects(Bucket = "my-bucket"), \(resp) resp$Contents)
```

### `paginate_sapply`:
Allows you to apply a function on each returning response, however the final result is simplified similar to `base::sapply`.
```r
library(paws)

svc <- s3(region = "us-west-2")

results <- paginate_sapply(
svc$list_objects(Bucket = "my-bucket"),
\(resp) resp$Contents,
simplify = T
)
```

## Customizing page Iterators

You can modify the operation by

* `MaxItems:`
Limits the maximum number of total returned items returned while paginating.
* `StartingToken:`
Can be used to modify the starting marker or token of a paginator. This argument if useful for resuming pagination from a previous token or starting pagination at a known position.
* `PageSize:`
Controls the number of items returned per page of each result.


### `paginate`
```r
library(paws)

svc <- s3(region = "us-west-2")

results <- paginate(svc$list_objects(Bucket = "my-bucket"), MaxItems = 10)
```

#### `paginate_lapply`
```r
library(paws)

svc <- s3(region = "us-west-2")

results <- paginate_lapply(svc$list_objects(Bucket = "my-bucket"), \(page) page$Contents)
```

#### `paginate_sapply`
```r
library(paws)

svc <- s3(region = "us-west-2")

results <- paginate_lapply(svc$list_objects(Bucket = "my-bucket"), \(page) page$Contents)
```

## Piping:

`paws` paginator support R native piping `|>`. However we currently don't support magrittr piping `%>%`.

```r
library(paws)
library(magrittr)

svc <- s3(region = "us-west-2")

# Will Work
results <- svc$list_objects(Bucket = "my-bucket") |> paginate(MaxItems = 10)

# Will error:
results <- svc$list_objects(Bucket = "my-bucket") %>% paginate(MaxItems = 10)
```


## Filtering results:

You can filter the paginator results by limiting the response for the paws operation. For example `list_objects` accepts `Prefix` parameter to filter page server-side before returning to `R`.

```r
library(paws)

svc <- s3(region = "us-west-2")

kwargs <- list(
Bucket='my-bucket',
Prefix='foo/baz'
)
result <- do.call(svc$list_objects, kwargs) |> paginate_lapply(\(page) page$Contents)
```
5 changes: 4 additions & 1 deletion make.paws/R/cran_category.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#' @include package.R service.R
NULL

.paws.common.import.version <- "paws.common (>= 0.5.9)"

# Make all category-level packages.
make_categories <- function(sdk_dir, out_dir, categories, service_names) {
for (category in categories) {
Expand All @@ -14,7 +16,7 @@ make_category <- function(category, service_names, sdk_dir, out_dir) {
services <- category$services
title <- category$title
description <- category$description
imports <- "paws.common (>= 0.5.4)"
imports <- .paws.common.import.version
version <- get_version(sdk_dir)

if (is.null(name) || is.null(title) || is.null(description)) {
Expand All @@ -34,6 +36,7 @@ make_category <- function(category, service_names, sdk_dir, out_dir) {
for (service in services) {
copy_files(service_names[[service]], from = sdk_dir, to = package_dir)
}
copy_files("reexports", from = sdk_dir, to = package_dir)
write_documentation(package_dir)
}

Expand Down
2 changes: 2 additions & 0 deletions make.paws/R/cran_collection.R
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ write_source_collection <- function(sdk_dir,
}
}
write_list(clients, file.path(out_dir, "R", "paws.R"))
write_list(make_reexports(), file.path(out_dir, "R", "reexports_paws.common.R"))
}

# Add the category packages to the DESCRIPTION file's Imports.
Expand All @@ -70,6 +71,7 @@ write_source_collection <- function(sdk_dir,
# generate the package.
write_imports_collection <- function(path, version, imports) {
packages <- sprintf("%s (>= %s)", imports, version)
packages <- c(packages, .paws.common.import.version)
desc::desc_set(
Imports = paste0(packages, collapse = ","),
file = file.path(path, "DESCRIPTION"),
Expand Down
27 changes: 25 additions & 2 deletions make.paws/R/operations.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#' @include templates.R
#' @include utils.R
NULL

operation_file_template <- template(
Expand Down Expand Up @@ -36,7 +37,7 @@ operation_template <- template(
name = ${operation_name},
http_method = ${http_method},
http_path = ${http_path},
paginator = list()
paginator = ${paginator}
)
input <- .${service}$${operation_input}
output <- .${service}$${operation_output}
Expand All @@ -62,10 +63,32 @@ make_operation <- function(operation, api, doc_maker) {
operation_input = get_operation_input(operation, api),
operation_output = get_operation_output(operation),
http_method = quoted(operation$http$method),
http_path = quoted(operation$http$requestUri)
http_path = quoted(operation$http$requestUri),
paginator = set_paginator(operation$paginators)
)
}


set_paginator <- function(paginator) {
if (!is.null(paginator)) {
output_token <- paginator$output_token
if (!is.null(output_token)) {
for (i in seq_along(output_token)) {
# output_token[[i]] <- strsplit(output_token[[i]], " ")[[1]][[1]]
output_token[i] <- list(trimws(strsplit(output_token[[i]], split = "||", fixed = T)[[1]]))
}
paginator$output_token <- unlist(output_token, use.names = FALSE)
paginator$input_token <- rep_len(
paginator$input_token,
length(paginator$output_token)
)
}
paste(trimws(deparse(paginator)), collapse = " ")
} else {
"list()"
}
}

# Override operation name from extdata/operation_name_override.yml
operation_name_override <- function(operation_name) {
path <- system_file(
Expand Down
13 changes: 13 additions & 0 deletions make.paws/R/process_api.R
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ make_code_files <- function(api) {
result$interfaces <- make_interfaces_files(api)
result$service <- make_service_files(api)
result$custom <- make_custom_operations_files(api)
result$reexports <- make_reexports()
return(result)
}

Expand Down Expand Up @@ -78,11 +79,23 @@ make_custom_operations_files <- function(api) {
return(result)
}

make_reexports <- function() {
result <- list()
from <- system_file("templates/reexports_paws.common.R", package = methods::getPackageName())
filename <- "reexports_paws.common.R"
if (from != "" && file.exists(from)) {
contents <- readLines(from)
result[[file.path(CODE_DIR, filename)]] <- paste(contents, collapse = "\n")
}
return(result)
}

make_docs_files <- function(api) {
result <- list()
result$operations <- make_operations_files(api, doc_maker = make_docs_long)
result$service <- make_service_files(api)
result$custom <- make_custom_operations_files(api)
result$reexports <- make_reexports()
return(result)
}

Expand Down
14 changes: 14 additions & 0 deletions make.paws/R/read_api.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ read_api <- function(api_name, path) {
examples <- jsonlite::read_json(files$examples)
api <- merge_examples(api, examples$examples)
}
if (!is.null(files$paginators)) {
paginators <- jsonlite::read_json(files$paginators)
api <- merge_paginators(api, paginators$pagination)
}
region_config <- jsonlite::read_json(region_config_path)
api <- merge_region_config(api, region_config)
api <- fix_region_config(api)
Expand Down Expand Up @@ -48,6 +52,16 @@ merge_examples <- function(api, examples) {
return(api)
}

# Returns an API object with paginators merged into the corresponding operations.
merge_paginators <- function(api, paginators) {
for (name in names(paginators)) {
operation <- api$operations[[name]]
operation[["paginators"]] <- paginators[[name]]
api$operations[[name]] <- operation
}
return(api)
}

# Returns an API object with region config info attached. Region config info
# lists endpoints for each service and region, if different from the default.
merge_region_config <- function(api, region_config) {
Expand Down
23 changes: 16 additions & 7 deletions make.paws/R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ parse_operations <- function(text) {
ids <- rep(NA, length(text))
id <- 1
for (i in seq_along(text)) {
if (i > 1 && startsWith(text[i], "#'") && !startsWith(text[i-1], "#'")) {
if (i > 1 && startsWith(text[i], "#'") && !startsWith(text[i - 1], "#'")) {
id <- id + 1
}
ids[i] <- id
Expand All @@ -35,9 +35,11 @@ parse_operation <- function(text) {
comment_lines <- startsWith(text, "#'")
comment <- text[comment_lines]
code <- text[!comment_lines]
if (length(code) == 0 || all(code == "") || code[1] == "NULL") return(NULL)
if (length(code) == 0 || all(code == "") || code[1] == "NULL") {
return(NULL)
}
func <- strsplit(code[1], " ")[[1]][1]
name <- substring(func, regexpr("_", func)+1)
name <- substring(func, regexpr("_", func) + 1)
operation <- list(
name = name,
http = list(),
Expand Down Expand Up @@ -65,14 +67,18 @@ system_file <- function(..., package = "base") {
pkg_path <- find.package(package)
subfolder <- list(...)
if (length(subfolder) > 0) {
if (subfolder[[1]] == "src")
if (subfolder[[1]] == "src") {
subfolder[[1]] <- "R"
else
} else {
subfolder <- c("inst", subfolder)
}
}
path <- do.call(file.path, c(pkg_path, subfolder))
if (file.exists(path)) return(path)
else return("")
if (file.exists(path)) {
return(path)
} else {
return("")
}
}
}

Expand Down Expand Up @@ -125,3 +131,6 @@ get_url <- function(url, tries = 3) {
return(NULL)
})
}

# helper function to make it easy to replace NULLs with default value
`%||%` <- function(x, y) if (is.null(x)) y else x
23 changes: 23 additions & 0 deletions make.paws/inst/templates/reexports_paws.common.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#' @importFrom paws.common paginate
#' @export
paws.common::paginate

#' @importFrom paws.common paginate_lapply
#' @export
paws.common::paginate_lapply

#' @importFrom paws.common paginate_sapply
#' @export
paws.common::paginate_sapply

#' @importFrom paws.common config
#' @export
paws.common::config

#' @importFrom paws.common credentials
#' @export
paws.common::credentials

#' @importFrom paws.common creds
#' @export
paws.common::creds
3 changes: 2 additions & 1 deletion paws.common/DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Roxygen: list(markdown = TRUE, roclets = c("rd", "namespace", "collate"))
RoxygenNote: 7.2.3
Collate:
'RcppExports.R'
'util.R'
'struct.R'
'handlers.R'
'logging.R'
Expand All @@ -55,7 +56,6 @@ Collate:
'service.R'
'custom_dynamodb.R'
'custom_rds.R'
'util.R'
'xmlutil.R'
'stream.R'
'custom_s3.R'
Expand All @@ -70,6 +70,7 @@ Collate:
'idempotency.R'
'jsonutil.R'
'onLoad.R'
'paginate.R'
'populate.R'
'populateutil.R'
'tags.R'
Expand Down
3 changes: 3 additions & 0 deletions paws.common/NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export(new_handlers)
export(new_operation)
export(new_request)
export(new_service)
export(paginate)
export(paginate_lapply)
export(paginate_sapply)
export(paws_config_log)
export(populate)
export(send_request)
Expand Down
1 change: 1 addition & 0 deletions paws.common/NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# paws.common 0.5.9
* add expiration parameter to creds
* add signature_version to config
* add the ability to paginate paws methods (#30)

# paws.common 0.5.8
* fix mismatch apparent method as.list.struct (#634)
Expand Down
Loading
Loading