diff --git a/R/RequestPattern.R b/R/RequestPattern.R index b600c4a..bb0e988 100644 --- a/R/RequestPattern.R +++ b/R/RequestPattern.R @@ -25,8 +25,8 @@ #' #' # uri with query parameters #' (x <- RequestPattern$new( -#' method = "get", uri = "https://httpbin.org/get", -#' query = list(foo = "bar") +#' method = "get", uri = "https://httpbin.org/get", +#' query = list(foo = "bar") #' )) #' x$to_s() #' ## query params included in url, not separately @@ -35,34 +35,43 @@ #' )) #' x$to_s() #' x$query_params -#' +#' #' # just headers (via setting method=any & uri_regex=.+) #' headers <- list( -#' 'User-Agent' = 'Apple', -#' 'Accept-Encoding' = 'gzip, deflate', -#' 'Accept' = 'application/json, text/xml, application/xml, */*') +#' "User-Agent" = "Apple", +#' "Accept-Encoding" = "gzip, deflate", +#' "Accept" = "application/json, text/xml, application/xml, */*" +#' ) #' x <- RequestPattern$new( -#' method = "any", -#' uri_regex = ".+", -#' headers = headers) +#' method = "any", +#' uri_regex = ".+", +#' headers = headers +#' ) #' x$to_s() -#' rs <- RequestSignature$new(method = "any", uri = "http://foo.bar", -#' options = list(headers = headers)) +#' rs <- RequestSignature$new( +#' method = "any", uri = "http://foo.bar", +#' options = list(headers = headers) +#' ) #' rs #' x$matches(rs) -#' +#' #' # body -#' x <- RequestPattern$new(method = "post", uri = "httpbin.org/post", -#' body = list(y = crul::upload(system.file("CITATION")))) +#' x <- RequestPattern$new( +#' method = "post", uri = "httpbin.org/post", +#' body = list(y = crul::upload(system.file("CITATION"))) +#' ) #' x$to_s() -#' rs <- RequestSignature$new(method = "post", uri = "http://httpbin.org/post", +#' rs <- RequestSignature$new( +#' method = "post", uri = "http://httpbin.org/post", #' options = list( -#' body = list(y = crul::upload(system.file("CITATION"))))) +#' body = list(y = crul::upload(system.file("CITATION"))) +#' ) +#' ) #' rs #' x$matches(rs) #' } RequestPattern <- R6::R6Class( - 'RequestPattern', + "RequestPattern", public = list( #' @field method_pattern xxx method_pattern = NULL, @@ -84,7 +93,6 @@ RequestPattern <- R6::R6Class( #' @return A new `RequestPattern` object initialize = function(method, uri = NULL, uri_regex = NULL, query = NULL, body = NULL, headers = NULL) { - if (is.null(uri) && is.null(uri_regex)) { stop("one of uri or uri_regex is required", call. = FALSE) } @@ -97,8 +105,9 @@ RequestPattern <- R6::R6Class( } self$uri_pattern$add_query_params(query) self$body_pattern <- if (!is.null(body)) BodyPattern$new(pattern = body) - self$headers_pattern <- if (!is.null(headers)) + self$headers_pattern <- if (!is.null(headers)) { HeadersPattern$new(pattern = headers) + } # FIXME: all private methods used in the below line, see if needed or remove # if (length(options)) private$assign_options(options) }, @@ -109,7 +118,7 @@ RequestPattern <- R6::R6Class( matches = function(request_signature) { assert(request_signature, "RequestSignature") c_type <- if (!is.null(request_signature$headers)) request_signature$headers$`Content-Type` else NULL - if (!is.null(c_type)) c_type <- strsplit(c_type, ';')[[1]][1] + if (!is.null(c_type)) c_type <- strsplit(c_type, ";")[[1]][1] self$method_pattern$matches(request_signature$method) && self$uri_pattern$matches(request_signature$uri) && (is.null(self$body_pattern) || self$body_pattern$matches(request_signature$body, c_type %||% "")) && @@ -127,7 +136,6 @@ RequestPattern <- R6::R6Class( )) } ), - private = list( # assign_options = function(options) { # #self$validate_keys(options, 'body', 'headers', 'query', 'basic_auth') @@ -151,16 +159,14 @@ RequestPattern <- R6::R6Class( # } # } # }, - set_basic_auth_as_headers = function(options) { - if ('basic_auth' %in% names(options)) { + if ("basic_auth" %in% names(options)) { private$validate_basic_auth(options$basic_auth) options$headers <- list() options$headers$Authorization <- private$make_basic_auth(options$basic_auth[1], options$basic_auth[2]) } }, - validate_basic_auth = function(x) { if (!inherits(x, "list") || length(unique(unname(unlist(x)))) == 1) { stop( @@ -169,7 +175,6 @@ RequestPattern <- R6::R6Class( ) } }, - make_basic_auth = function(x, y) { jsonlite::base64_enc(paste0(x, ":", y)) } @@ -186,7 +191,7 @@ RequestPattern <- R6::R6Class( #' x$pattern #' x$matches(method = "post") #' x$matches(method = "POST") -#' +#' #' # all matches() calls should be TRUE #' (x <- MethodPattern$new(pattern = "any")) #' x$pattern @@ -194,7 +199,7 @@ RequestPattern <- R6::R6Class( #' x$matches(method = "GET") #' x$matches(method = "HEAD") MethodPattern <- R6::R6Class( - 'MethodPattern', + "MethodPattern", public = list( #' @field pattern (character) an http method pattern = NULL, @@ -245,17 +250,18 @@ MethodPattern <- R6::R6Class( #' x$pattern #' x$matches(list(`hello-world` = "yep")) #' x$matches(list(`hello-worlds` = "yep")) -#' +#' #' headers <- list( -#' 'User-Agent' = 'Apple', -#' 'Accept-Encoding' = 'gzip, deflate', -#' 'Accept' = 'application/json, text/xml, application/xml, */*') +#' "User-Agent" = "Apple", +#' "Accept-Encoding" = "gzip, deflate", +#' "Accept" = "application/json, text/xml, application/xml, */*" +#' ) #' (x <- HeadersPattern$new(pattern = headers)) #' x$to_s() #' x$pattern #' x$matches(headers) HeadersPattern <- R6::R6Class( - 'HeadersPattern', + "HeadersPattern", public = list( #' @field pattern a list pattern = NULL, @@ -277,7 +283,9 @@ HeadersPattern <- R6::R6Class( if (self$empty_headers(self$pattern)) { self$empty_headers(headers) } else { - if (self$empty_headers(headers)) return(FALSE) + if (self$empty_headers(headers)) { + return(FALSE) + } headers <- private$normalize_headers(headers) out <- c() for (i in seq_along(self$pattern)) { @@ -299,7 +307,6 @@ HeadersPattern <- R6::R6Class( #' @return a string to_s = function() hdl_lst2(self$pattern) ), - private = list( normalize_headers = function(x) { # normalize names @@ -335,29 +342,35 @@ HeadersPattern <- R6::R6Class( #' z <- BodyPattern$new(pattern = list(foo = "bar", a = 5)) #' z$pattern #' z$matches(bb$body) -#' +#' #' # uploads in bodies #' ## upload NOT in a list #' bb <- RequestSignature$new( #' method = "post", uri = "https:/httpbin.org/post", -#' options = list(body = crul::upload(system.file("CITATION")))) +#' options = list(body = crul::upload(system.file("CITATION"))) +#' ) #' bb$body -#' z <- BodyPattern$new(pattern = -#' crul::upload(system.file("CITATION"))) +#' z <- BodyPattern$new( +#' pattern = +#' crul::upload(system.file("CITATION")) +#' ) #' z$pattern #' z$matches(bb$body) -#' +#' #' ## upload in a list #' bb <- RequestSignature$new( #' method = "post", uri = "https:/httpbin.org/post", -#' options = list(body = list(y = crul::upload(system.file("CITATION"))))) +#' options = list(body = list(y = crul::upload(system.file("CITATION")))) +#' ) #' bb$body -#' z <- BodyPattern$new(pattern = -#' list(y = crul::upload(system.file("CITATION")))) +#' z <- BodyPattern$new( +#' pattern = +#' list(y = crul::upload(system.file("CITATION"))) +#' ) #' z$pattern #' z$matches(bb$body) BodyPattern <- R6::R6Class( - 'BodyPattern', + "BodyPattern", public = list( #' @field pattern a list pattern = NULL, @@ -366,10 +379,11 @@ BodyPattern <- R6::R6Class( #' @param pattern (list) a body object #' @return A new `BodyPattern` object initialize = function(pattern) { - if (inherits(pattern, "form_file")) + if (inherits(pattern, "form_file")) { self$pattern <- unclass(pattern) - else + } else { self$pattern <- pattern + } }, #' @description Match a request body pattern against a pattern @@ -378,7 +392,9 @@ BodyPattern <- R6::R6Class( #' @return a boolean matches = function(body, content_type = "") { if (inherits(self$pattern, "list")) { - if (length(self$pattern) == 0) return(TRUE) + if (length(self$pattern) == 0) { + return(TRUE) + } private$matching_hashes(private$body_as_hash(body, content_type), self$pattern) } else { (private$empty_string(self$pattern) && private$empty_string(body)) || all(self$pattern == body) @@ -389,34 +405,41 @@ BodyPattern <- R6::R6Class( #' @return a string to_s = function() self$pattern ), - private = list( empty_string = function(string) { is.null(string) || !nzchar(string) }, - matching_hashes = function(z, pattern) { - if (is.null(z)) return(FALSE) - if (!inherits(z, "list")) return(FALSE) - if (!all(sort(names(z)) %in% sort(names(pattern)))) return(FALSE) + if (is.null(z)) { + return(FALSE) + } + if (!inherits(z, "list")) { + return(FALSE) + } + if (!all(sort(names(z)) %in% sort(names(pattern)))) { + return(FALSE) + } for (i in seq_along(z)) { expected <- pattern[[names(z)[i]]] actual <- z[[i]] if (inherits(actual, "list") && inherits(expected, "list")) { - if (!private$matching_hashes(actual, expected)) return(FALSE) + if (!private$matching_hashes(actual, expected)) { + return(FALSE) + } } else { - if (!identical(as.character(actual), as.character(expected))) return(FALSE) + if (!identical(as.character(actual), as.character(expected))) { + return(FALSE) + } } } return(TRUE) }, - body_as_hash = function(body, content_type) { if (inherits(body, "form_file")) body <- unclass(body) bctype <- BODY_FORMATS[[content_type]] %||% "" - if (bctype == 'json') { + if (bctype == "json") { jsonlite::fromJSON(body, FALSE) - } else if (bctype == 'xml') { + } else if (bctype == "xml") { check_for_pkg("xml2") xml2::read_xml(body) } else { @@ -427,16 +450,16 @@ BodyPattern <- R6::R6Class( ) BODY_FORMATS <- list( - 'text/xml' = 'xml', - 'application/xml' = 'xml', - 'application/json' = 'json', - 'text/json' = 'json', - 'application/javascript' = 'json', - 'text/javascript' = 'json', - 'text/html' = 'html', - 'application/x-yaml' = 'yaml', - 'text/yaml' = 'yaml', - 'text/plain' = 'plain' + "text/xml" = "xml", + "application/xml" = "xml", + "application/json" = "json", + "text/json" = "json", + "application/javascript" = "json", + "text/javascript" = "json", + "text/html" = "html", + "application/x-yaml" = "yaml", + "text/yaml" = "yaml", + "text/plain" = "plain" ) #' @title UriPattern @@ -485,7 +508,7 @@ BODY_FORMATS <- list( #' z$pattern #' z$matches("http://foobar.com?pizza=cheese&cheese=cheddar") # TRUE #' z$matches("http://foobar.com?pizza=cheese&cheese=swiss") # FALSE -#' +#' #' # query parameters in the uri #' (z <- UriPattern$new(pattern = "https://httpbin.org/get?stuff=things")) #' z$add_query_params() # have to run this method to gather query params @@ -567,17 +590,20 @@ UriPattern <- R6::R6Class( #' @return a boolean matches = function(uri) { uri <- normalize_uri(uri, self$regex) - if (self$regex) + if (self$regex) { grepl(self$pattern, uri) - else + } else { self$pattern_matches(uri) && self$query_params_matches(uri) + } }, #' @description Match a URI #' @param uri (character) a uri #' @return a boolean pattern_matches = function(uri) { - if (!self$regex) return(just_uri(uri) == just_uri(self$pattern)) # not regex + if (!self$regex) { + return(just_uri(uri) == just_uri(self$pattern)) + } # not regex grepl(drop_query_params(self$pattern), just_uri(uri)) # regex }, @@ -612,7 +638,9 @@ UriPattern <- R6::R6Class( #' @return named list, or `NULL` if no query parameters extract_query = function(uri) { params <- parse_a_url(uri)$parameter - if (all(is.na(params))) return(NULL) + if (all(is.na(params))) { + return(NULL) + } params }, @@ -620,19 +648,23 @@ UriPattern <- R6::R6Class( #' @param query_params (list|character) list or character #' @return nothing returned, updates uri pattern add_query_params = function(query_params) { - if (self$regex) return(NULL) + if (self$regex) { + return(NULL) + } if (missing(query_params) || is.null(query_params)) { self$query_params <- self$extract_query(self$pattern) } else { self$query_params <- query_params self$partial <- attr(query_params, "partial_match") %||% FALSE - self$partial_type <- attr(query_params, "partial_type") + self$partial_type <- attr(query_params, "partial_type") if ( inherits(query_params, "list") || - inherits(query_params, "character") + inherits(query_params, "character") ) { - pars <- paste0(unname(Map(function(x, y) paste(x, esc(y), sep = "="), - names(query_params), query_params)), collapse = "&") + pars <- paste0(unname(Map( + function(x, y) paste(x, esc(y), sep = "="), + names(query_params), query_params + )), collapse = "&") self$pattern <- paste0(self$pattern, "?", pars) } } @@ -654,7 +686,7 @@ UriPattern <- R6::R6Class( add_scheme <- function(x) { if (is.na(urltools::url_parse(x)$scheme)) { - paste0('https?://', x) + paste0("https?://", x) } else { x } @@ -663,11 +695,15 @@ esc <- function(x) curl::curl_escape(x) normalize_uri <- function(x, regex = FALSE) { x <- prune_trailing_slash(x) x <- prune_port(x) - if (!regex) - if (is.na(urltools::url_parse(x)$scheme)) - x <- paste0('http://', x) + if (!regex) { + if (is.na(urltools::url_parse(x)$scheme)) { + x <- paste0("http://", x) + } + } tmp <- urltools::url_parse(x) - if (is.na(tmp$path)) return(x) + if (is.na(tmp$path)) { + return(x) + } if (!regex) tmp$path <- esc(tmp$path) urltools::url_compose(tmp) } @@ -696,7 +732,8 @@ parse_a_url <- function(url) { strsplit(tmp$parameter, "&")[[1]], function(x) { z <- strsplit(x, split = "=")[[1]] as.list(stats::setNames(z[2], z[1])) - }), + } + ), recursive = FALSE ) } @@ -706,7 +743,7 @@ parse_a_url <- function(url) { just_uri <- function(x) { z <- urltools::url_parse(x) - z$parameter <- NA_character_ + z$parameter <- NA_character_ urltools::url_compose(z) } diff --git a/R/StubbedRequest.R b/R/StubbedRequest.R index 7d86368..458edb5 100644 --- a/R/StubbedRequest.R +++ b/R/StubbedRequest.R @@ -13,7 +13,7 @@ #' x$put(z) #' x$count() StubCounter <- R6::R6Class( - 'StubCounter', + "StubCounter", public = list( #' @field hash (list) a list for internal use only, with elements #' `key`, `sig`, and `count` @@ -48,11 +48,11 @@ StubCounter <- R6::R6Class( #' x <- StubbedRequest$new(method = "get", uri = "api.crossref.org") #' x$method #' x$uri -#' x$with(headers = list('User-Agent' = 'R', apple = "good")) +#' x$with(headers = list("User-Agent" = "R", apple = "good")) #' x$to_return(status = 200, body = "foobar", headers = list(a = 5)) #' x #' x$to_s() -#' +#' #' # query #' x <- StubbedRequest$new(method = "get", uri = "httpbin.org") #' x$with(query = list(a = 5)) @@ -85,8 +85,10 @@ StubCounter <- R6::R6Class( #' x #' #' x <- StubbedRequest$new(method = "get", uri = "api.crossref.org") -#' x$to_return(status = 200, body = charToRaw("foo bar"), -#' headers = list(a = 5)) +#' x$to_return( +#' status = 200, body = charToRaw("foo bar"), +#' headers = list(a = 5) +#' ) #' x$to_s() #' x #' @@ -108,8 +110,10 @@ StubCounter <- R6::R6Class( #' # payload written to file during mocked response creation #' x <- StubbedRequest$new(method = "get", uri = "api.crossref.org") #' f <- tempfile() -#' x$to_return(status = 200, body = mock_file(f, "{\"foo\": \"bar\"}"), -#' headers = list(a = 5)) +#' x$to_return( +#' status = 200, body = mock_file(f, "{\"foo\": \"bar\"}"), +#' headers = list(a = 5) +#' ) #' x #' x$to_s() #' unlink(f) @@ -201,30 +205,47 @@ StubbedRequest <- R6::R6Class( cat(paste0(" uri: ", self$uri %||% self$uri_regex), sep = "\n") cat(" with: ", sep = "\n") cat(paste0(" query: ", hdl_lst(self$query)), sep = "\n") - if (is.null(self$body)) + if (is.null(self$body)) { cat(" body: ", sep = "\n") - else - cat(sprintf(" body (class: %s): %s", class(self$body)[1L], - hdl_lst(self$body)), sep = "\n") - cat(paste0(" request_headers: ", - hdl_lst(self$request_headers)), - sep = "\n") + } else { + cat(sprintf( + " body (class: %s): %s", class(self$body)[1L], + hdl_lst(self$body) + ), sep = "\n") + } + cat( + paste0( + " request_headers: ", + hdl_lst(self$request_headers) + ), + sep = "\n" + ) cat(" to_return: ", sep = "\n") rs <- self$responses_sequences for (i in seq_along(rs)) { cat(paste0(" - status: ", hdl_lst(rs[[i]]$status)), - sep = "\n") + sep = "\n" + ) cat(paste0(" body: ", hdl_lst(rs[[i]]$body)), - sep = "\n") - cat(paste0(" response_headers: ", - hdl_lst(rs[[i]]$headers)), - sep = "\n") + sep = "\n" + ) + cat( + paste0( + " response_headers: ", + hdl_lst(rs[[i]]$headers) + ), + sep = "\n" + ) cat(paste0(" should_timeout: ", rs[[i]]$timeout), sep = "\n") - cat(paste0(" should_raise: ", - if (rs[[i]]$raise) + cat(paste0( + " should_raise: ", + if (rs[[i]]$raise) { paste0(vapply(rs[[i]]$exceptions, "[[", "", "classname"), - collapse = ", ") - else "FALSE" + collapse = ", " + ) + } else { + "FALSE" + } ), sep = "\n") } }, @@ -243,7 +264,7 @@ StubbedRequest <- R6::R6Class( self$body <- body self$basic_auth <- basic_auth if (!is.null(basic_auth)) { - headers <- c(prep_auth(paste0(basic_auth, collapse = ':')), headers) + headers <- c(prep_auth(paste0(basic_auth, collapse = ":")), headers) } self$request_headers <- headers }, @@ -259,8 +280,9 @@ StubbedRequest <- R6::R6Class( body <- if (inherits(body, "connection")) { bod_sum <- summary(body) close.connection(body) - if (bod_sum$class != "file") + if (bod_sum$class != "file") { stop("'to_return' only supports connections of type 'file'") + } structure(bod_sum$description, type = "file") } else { body @@ -274,9 +296,13 @@ StubbedRequest <- R6::R6Class( raw() } else { webmockr_stub_registry$remove_request_stub(self) - stop(paste0("Unknown type of `body`: ", - "must be NULL, FALSE, character, raw or list; stub removed"), - call. = FALSE) + stop( + paste0( + "Unknown type of `body`: ", + "must be NULL, FALSE, character, raw or list; stub removed" + ), + call. = FALSE + ) } } else if (inherits(body, "raw")) { body @@ -291,10 +317,14 @@ StubbedRequest <- R6::R6Class( } } else if (!is.list(body)) { webmockr_stub_registry$remove_request_stub(self) - stop(paste0("Unknown type of `body`: ", - "must be numeric, NULL, FALSE, character, json, ", - "raw, list, or file connection; stub removed"), - call. = FALSE) + stop( + paste0( + "Unknown type of `body`: ", + "must be numeric, NULL, FALSE, character, json, ", + "raw, list, or file connection; stub removed" + ), + call. = FALSE + ) } else { charToRaw(jsonlite::toJSON(body, auto_unbox = TRUE)) } @@ -344,14 +374,20 @@ StubbedRequest <- R6::R6Class( bd <- make_body(ret[[i]]$body) stt <- make_status(ret[[i]]$status) hed <- make_headers(ret[[i]]$headers) - strgs[i] <- sprintf("%s %s %s", + strgs[i] <- sprintf( + "%s %s %s", if (nzchar(paste0(bd, stt, hed))) paste("| to_return: ", bd, stt, hed) else "", if (ret[[i]]$timeout) "| should_timeout: TRUE" else "", - if (ret[[i]]$raise) - paste0("| to_raise: ", + if (ret[[i]]$raise) { + paste0( + "| to_raise: ", paste0(vapply(ret[[i]]$exceptions, "[[", "", "classname"), - collapse = ", ")) - else "" + collapse = ", " + ) + ) + } else { + "" + } ) } paste0(strgs, collapse = " ") @@ -367,14 +403,12 @@ StubbedRequest <- R6::R6Class( self$counter <- StubCounter$new() } ), - private = list( append_response = function(x) { self$responses_sequences <- cc(c(self$responses_sequences, list(x))) }, response = function(status = NULL, body = NULL, headers = NULL, - body_raw = NULL, timeout = FALSE, raise = FALSE, exceptions = list() - ) { + body_raw = NULL, timeout = FALSE, raise = FALSE, exceptions = list()) { list( status = status, body = body, @@ -395,7 +429,9 @@ basic_auth_header <- function(x) { return(paste0("Basic ", encoded)) } prep_auth <- function(x) { - if (is.null(x)) return(NULL) + if (is.null(x)) { + return(NULL) + } if (!is.null(x)) { list(Authorization = basic_auth_header(x)) } diff --git a/R/stub_request.R b/R/stub_request.R index 60337a2..439b543 100644 --- a/R/stub_request.R +++ b/R/stub_request.R @@ -56,22 +56,23 @@ #' #' # request headers #' stub_request("get", "https://httpbin.org/get") %>% -#' wi_th(headers = list('User-Agent' = 'R')) +#' wi_th(headers = list("User-Agent" = "R")) #' #' # request body #' stub_request("post", "https://httpbin.org/post") %>% -#' wi_th(body = list(foo = 'bar')) +#' wi_th(body = list(foo = "bar")) #' stub_registry() #' library(crul) #' x <- crul::HttpClient$new(url = "https://httpbin.org") #' crul::mock() -#' x$post('post', body = list(foo = 'bar')) +#' x$post("post", body = list(foo = "bar")) #' #' # add expectation with to_return #' stub_request("get", "https://httpbin.org/get") %>% #' wi_th( #' query = list(hello = "world"), -#' headers = list('User-Agent' = 'R')) %>% +#' headers = list("User-Agent" = "R") +#' ) %>% #' to_return(status = 200, body = "stuff", headers = list(a = 5)) #' #' # list stubs again @@ -83,7 +84,7 @@ #' # set stub an expectation to timeout #' stub_request("get", "https://httpbin.org/get") %>% to_timeout() #' x <- crul::HttpClient$new(url = "https://httpbin.org") -#' res <- x$get('get') +#' res <- x$get("get") #' #' # raise exception #' library(fauxpas) @@ -93,7 +94,7 @@ #' x <- crul::HttpClient$new(url = "https://httpbin.org") #' stub_request("get", "https://httpbin.org/get") %>% to_raise(HTTPBadGateway) #' crul::mock() -#' x$get('get') +#' x$get("get") #' #' # pass a list to .list #' z <- stub_request("get", "https://httpbin.org/get") @@ -101,31 +102,32 @@ #' #' # just body #' stub_request("any", uri_regex = ".+") %>% -#' wi_th(body = list(foo = 'bar')) +#' wi_th(body = list(foo = "bar")) #' ## with crul #' library(crul) #' x <- crul::HttpClient$new(url = "https://httpbin.org") #' crul::mock() -#' x$post('post', body = list(foo = 'bar')) -#' x$put('put', body = list(foo = 'bar')) +#' x$post("post", body = list(foo = "bar")) +#' x$put("put", body = list(foo = "bar")) #' ## with httr #' library(httr) #' httr_mock() -#' POST('https://example.com', body = list(foo = 'bar')) -#' PUT('https://google.com', body = list(foo = 'bar')) +#' POST("https://example.com", body = list(foo = "bar")) +#' PUT("https://google.com", body = list(foo = "bar")) #' #' #' # just headers #' headers <- list( -#' 'Accept-Encoding' = 'gzip, deflate', -#' 'Accept' = 'application/json, text/xml, application/xml, */*') +#' "Accept-Encoding" = "gzip, deflate", +#' "Accept" = "application/json, text/xml, application/xml, */*" +#' ) #' stub_request("any", uri_regex = ".+") %>% wi_th(headers = headers) #' library(crul) #' x <- crul::HttpClient$new(url = "https://httpbin.org", headers = headers) #' crul::mock() -#' x$post('post') -#' x$put('put', body = list(foo = 'bar')) -#' x$get('put', query = list(stuff = 3423234L)) +#' x$post("post") +#' x$put("put", body = list(foo = "bar")) +#' x$get("put", query = list(stuff = 3423234L)) #' #' # many responses #' ## the first response matches the first to_return call, and so on @@ -150,15 +152,15 @@ #' library(httr) #' ## matches #' stub_request("get", "https://hb.opencpu.org/get") %>% -#' wi_th(query = including(list(fruit = "pear"))) %>% -#' to_return(body = "matched on partial query!") +#' wi_th(query = including(list(fruit = "pear"))) %>% +#' to_return(body = "matched on partial query!") #' resp <- GET("https://hb.opencpu.org/get", query = list(fruit = "pear")) #' rawToChar(content(resp)) #' ## doesn't match #' stub_registry_clear() #' stub_request("get", "https://hb.opencpu.org/get") %>% -#' wi_th(query = list(fruit = "pear")) %>% -#' to_return(body = "didn't match, ugh!") +#' wi_th(query = list(fruit = "pear")) %>% +#' to_return(body = "didn't match, ugh!") #' # GET("https://hb.opencpu.org/get", query = list(fruit = "pear", meat = "chicken")) #' #' # clear all stubs diff --git a/R/wi_th.R b/R/wi_th.R index 0ca8ed9..4818465 100644 --- a/R/wi_th.R +++ b/R/wi_th.R @@ -5,7 +5,7 @@ #' @export #' @param .data input. Anything that can be coerced to a `StubbedRequest` class #' object -#' @param ... Comma separated list of named variables. accepts the following: +#' @param ... Comma separated list of named variables. accepts the following: #' `query`, `body`, `headers`, `basic_auth`. See Details. #' @param .list named list, has to be one of `query`, `body`, #' `headers` and/or `basic_auth`. An alternative to passing in via `...`. @@ -17,7 +17,7 @@ #' the stub #' @note see more examples in [stub_request()] #' @seealso [including()] -#' @details +#' @details #' Values for query, body, headers, and basic_auth: #' #' - query: (list) a named list. values are coerced to character @@ -32,12 +32,12 @@ #' mocking authenciation right now does not take into account the #' authentication type. We don't do any checking of the username/password #' except to detect edge cases where for example, the username/password -#' were probably not set by the user on purpose (e.g., a URL is +#' were probably not set by the user on purpose (e.g., a URL is #' picked up by an environment variable) -#' +#' #' Note that there is no regex matching on query, body, or headers. They #' are tested for matches in the following ways: -#' +#' #' - query: compare stubs and requests with `identical()`. this compares #' named lists, so both list names and values are compared #' - body: varies depending on the body format (list vs. character, etc.) @@ -67,21 +67,21 @@ #' #' # add headers - has to be a named list #' wi_th(req, headers = list(foo = "bar")) -#' wi_th(req, headers = list(`User-Agent` = "webmockr/v1", hello="world")) +#' wi_th(req, headers = list(`User-Agent` = "webmockr/v1", hello = "world")) #' #' # .list - pass in a named list instead #' wi_th(req, .list = list(body = list(foo = "bar"))) -#' +#' #' # basic authentication #' wi_th(req, basic_auth = c("user", "pass")) #' wi_th(req, basic_auth = c("user", "pass"), headers = list(foo = "bar")) -#' +#' #' # partial matching, query params #' ## including #' wi_th(req, query = including(list(foo = "bar"))) #' ## excluding #' wi_th(req, query = excluding(list(foo = "bar"))) -#' +#' #' # partial matching, body #' ## including #' wi_th(req, body = including(list(foo = "bar"))) @@ -95,7 +95,7 @@ wi_th <- function(.data, ..., .list = list()) { z <- c(z, .list) if ( !any(c("query", "body", "headers", "basic_auth") %in% names(z)) && - length(z) != 0 + length(z) != 0 ) { stop("'wi_th' only accepts query, body, headers, basic_auth") } diff --git a/R/zzz.R b/R/zzz.R index 9563842..0f014ae 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -5,7 +5,9 @@ cc <- function(x) Filter(Negate(is.null), x) is_nested <- function(x) { stopifnot(is.list(x)) for (i in x) { - if (is.list(i)) return(TRUE) + if (is.list(i)) { + return(TRUE) + } } return(FALSE) } @@ -30,23 +32,36 @@ has_attr <- function(x, at) { } hdl_lst <- function(x) { - if (is.null(x) || length(x) == 0) return("") - if (is.raw(x)) return(paste0("raw bytes, length: ", length(x))) - if (inherits(x, "form_file")) + if (is.null(x) || length(x) == 0) { + return("") + } + if (is.raw(x)) { + return(paste0("raw bytes, length: ", length(x))) + } + if (inherits(x, "form_file")) { return(sprintf("crul::upload(\"%s\", type=\"%s\")", x$path, x$type)) - if (inherits(x, "mock_file")) return(paste0("mock file, path: ", x$path)) + } + if (inherits(x, "mock_file")) { + return(paste0("mock file, path: ", x$path)) + } if (inherits(x, c("list", "partial"))) { if (is_nested(x)) { # substring(l2c(x), 1, 80) subs(l2c(x), 80) } else { - txt <- paste(names(x), subs(unname(unlist(x)), 20), sep = "=", - collapse = ", ") + txt <- paste(names(x), subs(unname(unlist(x)), 20), + sep = "=", + collapse = ", " + ) txt <- substring(txt, 1, 80) if (has_attr(x, "partial_match")) { - txt <- sprintf("%s(%s)", + txt <- sprintf( + "%s(%s)", switch(attr(x, "partial_type"), - include = "including", exclude = "excluding"), txt) + include = "including", + exclude = "excluding" + ), txt + ) } txt } @@ -56,18 +71,23 @@ hdl_lst <- function(x) { } hdl_lst2 <- function(x) { - if (is.null(x) || length(x) == 0) return("") - if (is.raw(x)) return(rawToChar(x)) - if (inherits(x, "form_file")) + if (is.null(x) || length(x) == 0) { + return("") + } + if (is.raw(x)) { + return(rawToChar(x)) + } + if (inherits(x, "form_file")) { return(sprintf("crul::upload(\"%s\", \"%s\")", x$path, x$type)) + } if (inherits(x, "list")) { - if (any(vapply(x, function(z) inherits(z, "form_file"), logical(1)))) + if (any(vapply(x, function(z) inherits(z, "form_file"), logical(1)))) { for (i in seq_along(x)) x[[i]] <- sprintf("crul::upload(\"%s\", \"%s\")", x[[i]]$path, x[[i]]$type) + } out <- vector(mode = "character", length = length(x)) for (i in seq_along(x)) { targ <- x[[i]] - out[[i]] <- paste(names(x)[i], switch( - class(targ)[1L], + out[[i]] <- paste(names(x)[i], switch(class(targ)[1L], character = if (grepl("upload", targ)) targ else sprintf('\"%s\"', targ), list = sprintf("list(%s)", hdl_lst2(targ)), targ @@ -103,16 +123,26 @@ url_builder <- function(uri, regex) { `%||%` <- function(x, y) { if ( is.null(x) || length(x) == 0 || all(nchar(x) == 0) || all(is.na(x)) - ) y else x + ) { + y + } else { + x + } } # tryCatch version of above `%|s|%` <- function(x, y) { z <- tryCatch(x) - if (inherits(z, "error")) return(y) + if (inherits(z, "error")) { + return(y) + } if ( is.null(z) || length(z) == 0 || all(nchar(z) == 0) || all(is.na(z)) - ) y else x + ) { + y + } else { + x + } } `!!` <- function(x) if (is.null(x) || is.na(x)) FALSE else TRUE @@ -121,21 +151,27 @@ assert <- function(x, y) { if (!is.null(x)) { if (!inherits(x, y)) { stop(deparse(substitute(x)), " must be of class ", - paste0(y, collapse = ", "), call. = FALSE) + paste0(y, collapse = ", "), + call. = FALSE + ) } } } assert_gte <- function(x, y) { if (!x >= y) { - stop(sprintf("%s must be greater than or equal to %s", - deparse(substitute(x)), y), call. = FALSE) + stop(sprintf( + "%s must be greater than or equal to %s", + deparse(substitute(x)), y + ), call. = FALSE) } } assert_eq <- function(x, y) { if (!is.null(x)) { if (!length(x) == y) { - stop(sprintf("length of %s must be equal to %s", - deparse(substitute(x)), y), call. = FALSE) + stop(sprintf( + "length of %s must be equal to %s", + deparse(substitute(x)), y + ), call. = FALSE) } } } @@ -160,11 +196,9 @@ crul_headers_parse <- function(x) do.call("c", lapply(x, crul_head_parse)) webmockr_crul_fetch <- function(x) { if (is.null(x$disk) && is.null(x$stream)) { curl::curl_fetch_memory(x$url$url, handle = x$url$handle) - } - else if (!is.null(x$disk)) { + } else if (!is.null(x$disk)) { curl::curl_fetch_disk(x$url$url, x$disk, handle = x$url$handle) - } - else { + } else { curl::curl_fetch_stream(x$url$url, x$stream, handle = x$url$handle) } } @@ -185,7 +219,7 @@ check_for_pkg <- function(x) { if (!requireNamespace(x, quietly = TRUE)) { stop(sprintf("Please install '%s'", x), call. = FALSE) } else { - invisible(TRUE) + invisible(TRUE) } } @@ -201,7 +235,9 @@ as_character <- function(x) { } last <- function(x) { - if (length(x) == 0) return(list()) + if (length(x) == 0) { + return(list()) + } x[[length(x)]] } @@ -220,8 +256,10 @@ vcr_cassette_inserted <- function() { check_redirect_setting <- function() { cs <- vcr::current_cassette() - stopifnot("record_separate_redirects must be logical" = - is.logical(cs$record_separate_redirects)) + stopifnot( + "record_separate_redirects must be logical" = + is.logical(cs$record_separate_redirects) + ) return(cs) } @@ -229,20 +267,25 @@ handle_separate_redirects <- function(req) { cs <- check_redirect_setting() if (cs$record_separate_redirects) { req$options$followlocation <- 0L - if (is.list(req$url)) + if (is.list(req$url)) { curl::handle_setopt(req$url$handle, followlocation = 0L) + } } return(req) } redirects_request <- function(x) { cs <- check_redirect_setting() - if (cs$record_separate_redirects) return(cs$request_handler$request_original) + if (cs$record_separate_redirects) { + return(cs$request_handler$request_original) + } x } redirects_response <- function(x) { cs <- check_redirect_setting() - if (cs$record_separate_redirects) return(last(cs$redirect_pool)[[1]]) + if (cs$record_separate_redirects) { + return(last(cs$redirect_pool)[[1]]) + } x } diff --git a/man/BodyPattern.Rd b/man/BodyPattern.Rd index ed1c03c..08e7e00 100644 --- a/man/BodyPattern.Rd +++ b/man/BodyPattern.Rd @@ -30,20 +30,26 @@ z$matches(bb$body) ## upload NOT in a list bb <- RequestSignature$new( method = "post", uri = "https:/httpbin.org/post", - options = list(body = crul::upload(system.file("CITATION")))) + options = list(body = crul::upload(system.file("CITATION"))) +) bb$body -z <- BodyPattern$new(pattern = - crul::upload(system.file("CITATION"))) +z <- BodyPattern$new( + pattern = + crul::upload(system.file("CITATION")) +) z$pattern z$matches(bb$body) ## upload in a list bb <- RequestSignature$new( method = "post", uri = "https:/httpbin.org/post", - options = list(body = list(y = crul::upload(system.file("CITATION"))))) + options = list(body = list(y = crul::upload(system.file("CITATION")))) +) bb$body -z <- BodyPattern$new(pattern = - list(y = crul::upload(system.file("CITATION")))) +z <- BodyPattern$new( + pattern = + list(y = crul::upload(system.file("CITATION"))) +) z$pattern z$matches(bb$body) } diff --git a/man/HeadersPattern.Rd b/man/HeadersPattern.Rd index ac2712a..bf3f086 100644 --- a/man/HeadersPattern.Rd +++ b/man/HeadersPattern.Rd @@ -31,9 +31,10 @@ x$matches(list(`hello-world` = "yep")) x$matches(list(`hello-worlds` = "yep")) headers <- list( - 'User-Agent' = 'Apple', - 'Accept-Encoding' = 'gzip, deflate', - 'Accept' = 'application/json, text/xml, application/xml, */*') + "User-Agent" = "Apple", + "Accept-Encoding" = "gzip, deflate", + "Accept" = "application/json, text/xml, application/xml, */*" +) (x <- HeadersPattern$new(pattern = headers)) x$to_s() x$pattern diff --git a/man/RequestPattern.Rd b/man/RequestPattern.Rd index 5885fa4..10768be 100644 --- a/man/RequestPattern.Rd +++ b/man/RequestPattern.Rd @@ -29,8 +29,8 @@ x$to_s() # uri with query parameters (x <- RequestPattern$new( - method = "get", uri = "https://httpbin.org/get", - query = list(foo = "bar") + method = "get", uri = "https://httpbin.org/get", + query = list(foo = "bar") )) x$to_s() ## query params included in url, not separately @@ -42,26 +42,35 @@ x$query_params # just headers (via setting method=any & uri_regex=.+) headers <- list( - 'User-Agent' = 'Apple', - 'Accept-Encoding' = 'gzip, deflate', - 'Accept' = 'application/json, text/xml, application/xml, */*') + "User-Agent" = "Apple", + "Accept-Encoding" = "gzip, deflate", + "Accept" = "application/json, text/xml, application/xml, */*" +) x <- RequestPattern$new( - method = "any", - uri_regex = ".+", - headers = headers) + method = "any", + uri_regex = ".+", + headers = headers +) x$to_s() -rs <- RequestSignature$new(method = "any", uri = "http://foo.bar", - options = list(headers = headers)) +rs <- RequestSignature$new( + method = "any", uri = "http://foo.bar", + options = list(headers = headers) +) rs x$matches(rs) # body -x <- RequestPattern$new(method = "post", uri = "httpbin.org/post", - body = list(y = crul::upload(system.file("CITATION")))) +x <- RequestPattern$new( + method = "post", uri = "httpbin.org/post", + body = list(y = crul::upload(system.file("CITATION"))) +) x$to_s() -rs <- RequestSignature$new(method = "post", uri = "http://httpbin.org/post", +rs <- RequestSignature$new( + method = "post", uri = "http://httpbin.org/post", options = list( - body = list(y = crul::upload(system.file("CITATION"))))) + body = list(y = crul::upload(system.file("CITATION"))) + ) +) rs x$matches(rs) } diff --git a/man/StubbedRequest.Rd b/man/StubbedRequest.Rd index 52f83b8..43bc357 100644 --- a/man/StubbedRequest.Rd +++ b/man/StubbedRequest.Rd @@ -11,7 +11,7 @@ stubbed request class underlying \code{\link[=stub_request]{stub_request()}} x <- StubbedRequest$new(method = "get", uri = "api.crossref.org") x$method x$uri -x$with(headers = list('User-Agent' = 'R', apple = "good")) +x$with(headers = list("User-Agent" = "R", apple = "good")) x$to_return(status = 200, body = "foobar", headers = list(a = 5)) x x$to_s() @@ -48,8 +48,10 @@ x$to_s() x x <- StubbedRequest$new(method = "get", uri = "api.crossref.org") -x$to_return(status = 200, body = charToRaw("foo bar"), - headers = list(a = 5)) +x$to_return( + status = 200, body = charToRaw("foo bar"), + headers = list(a = 5) +) x$to_s() x @@ -71,8 +73,10 @@ unlink(f) # payload written to file during mocked response creation x <- StubbedRequest$new(method = "get", uri = "api.crossref.org") f <- tempfile() -x$to_return(status = 200, body = mock_file(f, "{\"foo\": \"bar\"}"), - headers = list(a = 5)) +x$to_return( + status = 200, body = mock_file(f, "{\"foo\": \"bar\"}"), + headers = list(a = 5) +) x x$to_s() unlink(f) diff --git a/man/stub_request.Rd b/man/stub_request.Rd index 2416178..67075a8 100644 --- a/man/stub_request.Rd +++ b/man/stub_request.Rd @@ -76,22 +76,23 @@ stub_registry() # request headers stub_request("get", "https://httpbin.org/get") \%>\% - wi_th(headers = list('User-Agent' = 'R')) + wi_th(headers = list("User-Agent" = "R")) # request body stub_request("post", "https://httpbin.org/post") \%>\% - wi_th(body = list(foo = 'bar')) + wi_th(body = list(foo = "bar")) stub_registry() library(crul) x <- crul::HttpClient$new(url = "https://httpbin.org") crul::mock() -x$post('post', body = list(foo = 'bar')) +x$post("post", body = list(foo = "bar")) # add expectation with to_return stub_request("get", "https://httpbin.org/get") \%>\% wi_th( query = list(hello = "world"), - headers = list('User-Agent' = 'R')) \%>\% + headers = list("User-Agent" = "R") + ) \%>\% to_return(status = 200, body = "stuff", headers = list(a = 5)) # list stubs again @@ -103,7 +104,7 @@ stub_request("get", uri_regex = ".+ample\\\\..") # set stub an expectation to timeout stub_request("get", "https://httpbin.org/get") \%>\% to_timeout() x <- crul::HttpClient$new(url = "https://httpbin.org") -res <- x$get('get') +res <- x$get("get") # raise exception library(fauxpas) @@ -113,7 +114,7 @@ stub_request("get", "https://httpbin.org/get") \%>\% to_raise(HTTPAccepted, HTTP x <- crul::HttpClient$new(url = "https://httpbin.org") stub_request("get", "https://httpbin.org/get") \%>\% to_raise(HTTPBadGateway) crul::mock() -x$get('get') +x$get("get") # pass a list to .list z <- stub_request("get", "https://httpbin.org/get") @@ -121,31 +122,32 @@ wi_th(z, .list = list(query = list(foo = "bar"))) # just body stub_request("any", uri_regex = ".+") \%>\% - wi_th(body = list(foo = 'bar')) + wi_th(body = list(foo = "bar")) ## with crul library(crul) x <- crul::HttpClient$new(url = "https://httpbin.org") crul::mock() -x$post('post', body = list(foo = 'bar')) -x$put('put', body = list(foo = 'bar')) +x$post("post", body = list(foo = "bar")) +x$put("put", body = list(foo = "bar")) ## with httr library(httr) httr_mock() -POST('https://example.com', body = list(foo = 'bar')) -PUT('https://google.com', body = list(foo = 'bar')) +POST("https://example.com", body = list(foo = "bar")) +PUT("https://google.com", body = list(foo = "bar")) # just headers headers <- list( - 'Accept-Encoding' = 'gzip, deflate', - 'Accept' = 'application/json, text/xml, application/xml, */*') + "Accept-Encoding" = "gzip, deflate", + "Accept" = "application/json, text/xml, application/xml, */*" +) stub_request("any", uri_regex = ".+") \%>\% wi_th(headers = headers) library(crul) x <- crul::HttpClient$new(url = "https://httpbin.org", headers = headers) crul::mock() -x$post('post') -x$put('put', body = list(foo = 'bar')) -x$get('put', query = list(stuff = 3423234L)) +x$post("post") +x$put("put", body = list(foo = "bar")) +x$get("put", query = list(stuff = 3423234L)) # many responses ## the first response matches the first to_return call, and so on @@ -170,15 +172,15 @@ con$get("get")$parse("UTF-8") library(httr) ## matches stub_request("get", "https://hb.opencpu.org/get") \%>\% - wi_th(query = including(list(fruit = "pear"))) \%>\% - to_return(body = "matched on partial query!") + wi_th(query = including(list(fruit = "pear"))) \%>\% + to_return(body = "matched on partial query!") resp <- GET("https://hb.opencpu.org/get", query = list(fruit = "pear")) rawToChar(content(resp)) ## doesn't match stub_registry_clear() stub_request("get", "https://hb.opencpu.org/get") \%>\% - wi_th(query = list(fruit = "pear")) \%>\% - to_return(body = "didn't match, ugh!") + wi_th(query = list(fruit = "pear")) \%>\% + to_return(body = "didn't match, ugh!") # GET("https://hb.opencpu.org/get", query = list(fruit = "pear", meat = "chicken")) # clear all stubs diff --git a/man/wi_th.Rd b/man/wi_th.Rd index b8c17b2..ed2e569 100644 --- a/man/wi_th.Rd +++ b/man/wi_th.Rd @@ -83,7 +83,7 @@ wi_th(req, query = list(foo = "bar")) # add headers - has to be a named list wi_th(req, headers = list(foo = "bar")) -wi_th(req, headers = list(`User-Agent` = "webmockr/v1", hello="world")) +wi_th(req, headers = list(`User-Agent` = "webmockr/v1", hello = "world")) # .list - pass in a named list instead wi_th(req, .list = list(body = list(foo = "bar")))