gh icon indicating copy to clipboard operation
gh copied to clipboard

Generalize gh plumbing?

Open coatless opened this issue 4 years ago • 5 comments

I'm a huge fan of how gh parses and builds an HTTP request. As I rely more and more on web APIs, I'm quickly desiring a layer of abstract that allows for me to just copy and paste the endpoint information from the web API. e.g.

GET /api/v1/quiz_submissions/:quiz_submission_id/questions

https://canvas.instructure.com/doc/api/quiz_submissions.html

The function would then: 1. choose the appropriate verb and 2. parse with the parameters in the string. The present way that I'm accomplishing this is:

paste0("GET /api/v1/quiz_submissions/", quiz_submission_id,"/questions")
# or 
glue::glue("GET /api/v1/quiz_submissions/{{quiz_submission_id}}/questions")

Thus, to this end, would there be any objection if I worked on a package {rest} that generalized the request build held by gh? c.f.

https://github.com/r-lib/gh/blob/master/R/gh_request.R

coatless avatar Feb 14 '20 19:02 coatless

That would make a lot of sense. Somewhat related to #46 I think.

gaborcsardi avatar Feb 14 '20 21:02 gaborcsardi

This is also semi-related to what gargle does (but for Google APIs, specifically).

https://github.com/r-lib/gargle/blob/master/R/request-develop.R

request_develop() takes input from a user (probably already pre-processed by a wrapper function or package) via params and info about an endpoint (as published in JSON Discovery Documents, a Google API thing) and does light validation + sticking the info into all the right places (URL substitution, query params, body).

jennybc avatar Feb 14 '20 21:02 jennybc

This was also something we'd thought about for httr2

hadley avatar Mar 02 '20 17:03 hadley

With the extra COVID-19 workload decreasing, I've slowly started to hack away at this in https://github.com/coatless/rest

In particular, I've transformed the gh() call to rest() and reduced any GitHub-specific features (more work is needed here). As an example, an API could be called with:

rest::rest("GET /facts", .api_url = "https://cat-fact.herokuapp.com")
#> {
#>   "all": [
#>     {
#>       "_id": "5887e1d85c873e0011036889",
#>       "text": "Cats make about 100 different sounds. Dogs make only about 10.",
#>       "type": "cat",
#>       "user": {
#>         "_id": "5a9ac18c7478810ea6c06381",
#>         "name": {
#>           "first": "Alex",
#>           "last": "Wohlbruck"
#>         }
#>       },
#>       "upvotes": 10,
#>       "userUpvoted": {}
#>     },
#>     {
#>       "_id": "58e008b80aac31001185ed0d",
#>       "text": "Adult cats only meow to communicate with humans.",
#>       "type": "cat",
#>       "user": {
#>         "_id": "58e007480aac31001185ecef",
#>         "name": {
#>           "first": "Kasimir",
#>           "last": "Schulz"
#>         }
#>       },
#>       "upvotes": 9,
#>       "userUpvoted": {}
#>     }
#>   ]
#> }

I still have to solve the best way to register APIs within the package. The easiest way would probably be to have any API package create a wrapper around this function and supply appropriate token names. So, in the context of gh, that would be:

gh <- function(endpoint, ...) {
   rest::rest(endpoint,
              .api_url = "https://api.github.com",
              .token_name="GITHUB_PAT", ...)
}

Not sure if this aligns with everyone's previous thoughts in this issue ticket.

coatless avatar May 26 '20 20:05 coatless

I think it makes sense. Although, it is not quite clear how a generic solution can handle e.g. GitHub's pagination. There are probably other API dependent aspects as well.

gaborcsardi avatar May 27 '20 09:05 gaborcsardi

I think the generic handling is now in https://httr2.r-lib.org/reference/req_template.html.

hadley avatar Feb 06 '23 23:02 hadley