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

Tibblify paths #84

Merged
merged 4 commits into from
Mar 26, 2024
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
16 changes: 11 additions & 5 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
Package: rapid
Title: R 'API' Descriptions
Version: 0.0.0.9000
Version: 0.0.0.9003
Authors@R: c(
person("Jon", "Harmon", , "[email protected]", role = c("aut", "cre"),
comment = c(ORCID = "0000-0003-4781-4346")),
person("The Linux Foundation", role = "cph",
comment = "OpenAPI Specification")
)
Description: Convert an 'API' description ('APID'), such as one that follows
the 'OpenAPI Specification', to an R 'API' description object (a
"rapid"). The rapid object follows the 'OpenAPI Specification' to
Description: Convert an 'API' description ('APID'), such as one that
follows the 'OpenAPI Specification', to an R 'API' description object
(a "rapid"). The rapid object follows the 'OpenAPI Specification' to
make it easy to convert to and from 'API' documents.
License: MIT + file LICENSE
URL: https://jonthegeek.github.io/rapid/,
https://github.com/jonthegeek/rapid
BugReports: https://github.com/jonthegeek/rapid/issues
Depends:
R (>= 3.5.0)
Imports:
cli,
glue,
Expand All @@ -24,12 +26,15 @@ Imports:
S7 (>= 0.1.1),
snakecase,
stbl,
tibble,
tibblify,
xml2,
yaml
Suggests:
testthat (>= 3.0.0)
Remotes:
jonthegeek/stbl
jonthegeek/stbl,
mgirlich/tibblify#191
Config/testthat/edition: 3
Config/testthat/parallel: true
Encoding: UTF-8
Expand All @@ -38,6 +43,7 @@ RoxygenNote: 7.3.1
Collate:
'properties.R'
'security.R'
'paths.R'
'components-security_scheme_details.R'
'components-security_schemes.R'
'components.R'
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export(as_oauth2_implicit_flow)
export(as_oauth2_security_scheme)
export(as_oauth2_token_flow)
export(as_origin)
export(as_paths)
export(as_rapid)
export(as_reference)
export(as_schema)
Expand All @@ -34,6 +35,7 @@ export(class_oauth2_implicit_flow)
export(class_oauth2_security_scheme)
export(class_oauth2_token_flow)
export(class_origin)
export(class_paths)
export(class_rapid)
export(class_reference)
export(class_schema)
Expand All @@ -50,6 +52,7 @@ importFrom(S7,"prop<-")
importFrom(S7,S7_inherits)
importFrom(S7,class_any)
importFrom(S7,class_character)
importFrom(S7,class_data.frame)
importFrom(S7,class_factor)
importFrom(S7,class_list)
importFrom(S7,class_logical)
Expand Down
5 changes: 1 addition & 4 deletions R/components-security_schemes.R
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,7 @@ S7::method(length, class_security_schemes) <- function(x) {
#' )
#' )
#' )
as_security_schemes <- S7::new_generic(
"as_security_schemes",
"x"
)
as_security_schemes <- S7::new_generic("as_security_schemes", "x")

S7::method(
as_security_schemes,
Expand Down
105 changes: 105 additions & 0 deletions R/paths.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#' The available paths and operations for the API
#'
#' Holds the relative paths to the individual endpoints and their operations.
#' The path is appended to the URL from the [class_servers()] object in order to
#' construct the full URL. The paths may be empty.
#'
#' @param ... A data.frame, or arguments to pass to [tibble::tibble()].
#'
#' @return A `paths` S7 object with details about API endpoints.
#' @export
#'
#' @seealso [as_paths()] for coercing objects to `paths`.
#'
#' @examples
#' class_paths()
#' class_paths(
#' tibble::tibble(
#' endpoint = c("/endpoint1", "/endpoint2"),
#' operations = list(
#' tibble::tibble(operation_properties = 1:2),
#' tibble::tibble(operation_properties = 3:5)
#' )
#' )
#' )
class_paths <- S7::new_class(
"paths",
package = "rapid",
parent = class_data.frame,
constructor = function(...) {
if (...length() == 1 && is.data.frame(..1)) {
return(S7::new_object(tibble::as_tibble(..1)))
}
S7::new_object(tibble::tibble(...))
}
)

#' Coerce objects to paths
#'
#' `as_paths()` turns an existing object into a `paths` object. This is in
#' contrast with [class_paths()], which builds a `paths` object from individual
#' properties. In practice, [class_paths()] and `as_paths()` are currently
#' functionally identical. However, in the future, `as_paths()` will coerce
#' other valid objects to the expected shape.
#'
#' @inheritParams rlang::args_dots_empty
#' @inheritParams rlang::args_error_context
#' @param x The object to coerce. Must be empty or be a `data.frame()`.
#'
#' @return A `paths` object as returned by [class_paths()].
#' @export
#'
#' @examples
#' as_paths()
#' as_paths(mtcars)
as_paths <- S7::new_generic("as_paths", "x")

S7::method(as_paths, class_data.frame) <- function(x,
...,
arg = caller_arg(x),
call = caller_env()) {
class_paths(x)
}

S7::method(as_paths, class_any) <- function(x,
...,
arg = caller_arg(x),
call = caller_env()) {
as_api_object(x, class_paths, ..., arg = arg, call = call)
}

.parse_paths <- S7::new_generic(".parse_paths", "paths")

S7::method(.parse_paths, class_data.frame | class_paths) <- function(paths,
...) {
paths
}

S7::method(.parse_paths, class_list) <- function(paths,
openapi,
x,
call = caller_env()) {
if (!is.null(openapi) && openapi >= "3") {
return(.parse_openapi_spec(x, call = call))
}
return(tibble::tibble())
}

.parse_openapi_spec <- function(x, call = caller_env()) { # nocov start
rlang::try_fetch(
{
tibblify::parse_openapi_spec(x)
},
error = function(cnd) {
cli::cli_abort(
"Failed to parse paths from OpenAPI spec.",
class = "rapid_error_bad_tibblify",
call = call
)
}
)
} # nocov end

S7::method(.parse_paths, class_any) <- function(paths, ...) {
return(tibble::tibble())
}
1 change: 1 addition & 0 deletions R/rapid-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#' @importFrom rlang check_dots_empty
#' @importFrom S7 class_any
#' @importFrom S7 class_character
#' @importFrom S7 class_data.frame
#' @importFrom S7 class_factor
#' @importFrom S7 class_list
#' @importFrom S7 class_logical
Expand Down
9 changes: 7 additions & 2 deletions R/urls.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@

.url_fetch <- function(x) {
rlang::try_fetch(
jsonlite::read_json(x),
jsonlite::read_json(
x,
simplifyVector = TRUE,
simplifyDataFrame = FALSE,
simplifyMatrix = FALSE
),
error = function(e) {
yaml::read_yaml(url(x))
yaml::read_yaml(url(x)) # nocov
}
)
}
38 changes: 32 additions & 6 deletions R/zz-rapid.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#' @include info.R
#' @include servers.R
#' @include components.R
#' @include paths.R
#' @include security.R
NULL

Expand All @@ -12,10 +13,11 @@ NULL
#' @param info An `info` object defined by [class_info()].
#' @param servers A `servers` object defined by [class_servers()].
#' @param components A `components` object defined by [class_components()].
#' @param paths A `paths` object defined by [class_paths()].
#' @param security A `security` object defined by [class_security()].
#'
#' @return A `rapid` S7 object, with properties `info`, `servers`, `components`,
#' and `security`.
#' `paths`, and `security`.
#' @export
#'
#' @seealso [as_rapid()] for coercing objects to `rapid`.
Expand Down Expand Up @@ -58,19 +60,22 @@ class_rapid <- S7::new_class(
info = class_info,
servers = class_servers,
components = class_components,
paths = class_paths,
security = class_security
),
constructor = function(info = class_info(),
...,
servers = class_servers(),
components = class_components(),
paths = class_paths(),
security = class_security()) {
check_dots_empty()
S7::new_object(
S7::S7_object(),
info = as_info(info),
servers = as_servers(servers),
components = as_components(components),
paths = as_paths(paths),
security = as_security(security)
)
},
Expand All @@ -79,7 +84,7 @@ class_rapid <- S7::new_class(
validate_lengths(
self,
key_name = "info",
optional_any = c("components", "security", "servers")
optional_any = c("components", "paths", "security", "servers")
),
validate_in_specific(
values = self@security@name,
Expand All @@ -103,10 +108,11 @@ S7::method(length, class_rapid) <- function(x) {
#'
#' @inheritParams rlang::args_dots_empty
#' @inheritParams rlang::args_error_context
#' @param x The object to coerce. Must be empty or have names "info" and/or
#' "servers", or names that can be coerced to those names via
#' [snakecase::to_snake_case()]. Extra names are ignored. [url()] objects are
#' read with [jsonlite::fromJSON()] or [yaml::read_yaml()] before conversion.
#' @param x The object to coerce. Must be empty or have names "info", "servers",
#' "components", "paths", and/or "security", or names that can be coerced to
#' those names via [snakecase::to_snake_case()]. Extra names are ignored.
#' [url()] objects are read with [jsonlite::fromJSON()] or [yaml::read_yaml()]
#' before conversion.
#'
#' @return A `rapid` object as returned by [class_rapid()].
#' @export
Expand All @@ -127,6 +133,26 @@ S7::method(as_rapid, S7::new_S3_class("url")) <- function(x,
as_rapid(x, ..., arg = arg, call = call)
}

S7::method(as_rapid, class_list) <- function(x,
...,
arg = caller_arg(x),
call = caller_env()) {
x$paths <- .parse_paths(x$paths, x$openapi, x, call)
rlang::try_fetch(
{
x <- as_api_object(x, class_rapid, ..., arg = arg, call = call)
expand_servers(x)
},
rapid_error_missing_names = function(cnd) {
cli::cli_abort(
"{.arg x} must be comprised of properly formed, supported elements.",
class = "rapid_error_unsupported_elements",
parent = cnd
)
}
)
}

S7::method(as_rapid, class_any) <- function(x,
...,
arg = caller_arg(x),
Expand Down
4 changes: 4 additions & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ reference:
- as_oauth2_token_flow
- class_scopes
- as_scopes
- title: paths class
contents:
- class_paths
- as_paths
- title: security class
contents:
- class_security
Expand Down
27 changes: 27 additions & 0 deletions man/as_paths.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions man/as_rapid.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading