ghub.info (52448B)
1 This is ghub.info, produced by makeinfo version 6.7 from ghub.texi. 2 3 Copyright (C) 2017-2022 Jonas Bernoulli <jonas@bernoul.li> 4 5 You can redistribute this document and/or modify it under the terms 6 of the GNU General Public License as published by the Free Software 7 Foundation, either version 3 of the License, or (at your option) 8 any later version. 9 10 This document is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 INFO-DIR-SECTION Emacs 16 START-INFO-DIR-ENTRY 17 * Ghub: (ghub). Minuscule client library for the Github API. 18 END-INFO-DIR-ENTRY 19 20 21 File: ghub.info, Node: Top, Next: Introduction, Up: (dir) 22 23 Ghub User and Developer Manual 24 ****************************** 25 26 Ghub is an Emacs library that is used by various packages to access the 27 APIs of various instances of various Git forge implementations. 28 29 This manual is for Ghub version 3.5.6. 30 31 Copyright (C) 2017-2022 Jonas Bernoulli <jonas@bernoul.li> 32 33 You can redistribute this document and/or modify it under the terms 34 of the GNU General Public License as published by the Free Software 35 Foundation, either version 3 of the License, or (at your option) 36 any later version. 37 38 This document is distributed in the hope that it will be useful, 39 but WITHOUT ANY WARRANTY; without even the implied warranty of 40 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 41 General Public License for more details. 42 43 * Menu: 44 45 * Introduction:: 46 * Getting Started:: 47 * API:: 48 * Notes:: 49 * Function Index:: 50 * Variable Index:: 51 52 — The Detailed Node Listing — 53 54 Getting Started 55 56 * Basic Concepts, Arguments and Variables: Basic Concepts Arguments and Variables. 57 * Setting the Username:: 58 * Creating and Storing a Token:: 59 * Github Configuration Variables:: 60 61 Creating and Storing a Token 62 63 * Creating a Token:: 64 * Storing a Token:: 65 66 67 API 68 69 * Their APIs:: 70 * Making REST Requests:: 71 * Making GraphQL Requests:: 72 * Github Convenience Wrappers:: 73 * Non-Github Convenience Wrappers:: 74 75 Notes 76 77 * Using Ghub in Personal Scripts:: 78 * Using Ghub in Your Own Package:: 79 * Forge Limitations and Notes:: 80 81 82 83 File: ghub.info, Node: Introduction, Next: Getting Started, Prev: Top, Up: Top 84 85 1 Introduction 86 ************** 87 88 Ghub is an Emacs library that is used by various packages to access the 89 APIs of various instances of various Git forge implementations. 90 91 A forge is a web-based collaborative software platform for developing 92 and distributing computer applications. Examples include Github and 93 Gitlab. 94 95 96 File: ghub.info, Node: Getting Started, Next: API, Prev: Introduction, Up: Top 97 98 2 Getting Started 99 ***************** 100 101 This manual guides you through the steps that are necessary to use the 102 Forge package and/or to make a request using just Ghub itself, such as 103 this: 104 105 (ghub-request "GET" "/user") 106 107 To be able to do that, Ghub needs to know who you want to talk to, 108 who you are, and how you are going to prove the latter to the former. 109 110 Additionally Ghub wants to know on behalf of which Emacs package it 111 is making a request. So the question of "who is making the request" has 112 to be rephrased as "which human (or bot) is using what (Emacs) package 113 to make the request". If for example, the human known as "tarsius" is 114 using the ‘forge’ package, then that is represented in some places using 115 the string "tarsius^forge". 116 117 This package used to attempt to get the answers to these questions 118 using a setup wizard. Unfortunately that had to be removed because (a) 119 it only ever supported Github, (b) Github is about to remove support for 120 that on their end, (c) it did not always work, and (d) when it couldn’t 121 be used, or failed, then it made things _more_ complicated. 122 123 So now it is necessary for users to read some documentation and 124 because many things can go wrong, those instructions have to be fairly 125 detailed. You can of course skip over most of this, but if things go 126 wrong, then I would like to kindly request that you take another look 127 before asking me for help. 128 129 * Menu: 130 131 * Basic Concepts, Arguments and Variables: Basic Concepts Arguments and Variables. 132 * Setting the Username:: 133 * Creating and Storing a Token:: 134 * Github Configuration Variables:: 135 136 137 File: ghub.info, Node: Basic Concepts Arguments and Variables, Next: Setting the Username, Up: Getting Started 138 139 2.1 Basic Concepts, Arguments and Variables 140 =========================================== 141 142 Originally Ghub supported only Github but now it also supports Gitlab, 143 Gitea, Gogs and Bitbucket. For the historic reason just given, the 144 function ‘ghub-request’ defaults to acting on a ‘github’ forge, but can 145 be told to act on another forge using the FORGE argument. 146 147 The FORGE argument only specifies what kind of forge to act on, not 148 which instance. The HOST argument can be used to select the instance. 149 For some forges a default instance is defined: 150 151 • Forge ‘github’ defaults to host ‘api.github.com’. 152 • Forge ‘gitlab’ defaults to host ‘gitlab.com/api/v4’. 153 • Forge ‘bitbucket’ defaults to host ‘api.bitbucket.org/2.0’. 154 • No canonical host exists for the ‘gitea’ and ‘gogs’ forges and 155 ‘localhost:3000/api/v1’ is used as the default host in both cases. 156 157 Together the FORGE and HOST arguments specify the forge type and 158 instance. In addition to that, it is also necessary to specify on whose 159 behalf the request is being made, which can be done using the USERNAME 160 and AUTH arguments. For example: 161 162 (ghub-request "GET" "/user" nil 163 :forge 'github 164 :host "api.github.com" 165 :username "tarsius" 166 :auth 'forge) 167 168 Having to specify these arguments for every request is inconvenient. 169 Additional variables and convenience functions can be used to make that 170 unnecessary in most cases. 171 172 (But for debugging purposes the above explicit form is very useful. 173 You will obviously have to change the value of USERNAME and you should 174 use ‘ghub’ as AUTH when first trying this at home.) 175 176 These variables can be set globally and/or for a specific repository. 177 178 • For "api.github.com" (aka the API of <https://github.com>) the Git 179 variable ‘github.user’ specifies the user. 180 • For another ‘github’ instance the Git variable ‘github.HOST.user’ 181 specifies the user. The HOST in that variable name is the same as 182 the value of the HOST argument of the called function. 183 • Instead of specifying the HOST in every function call, the Git 184 variable ‘github.host’ can be used. This should only be set 185 locally. 186 187 These ‘github’ specific variables are discussed in more detail in 188 *note Github Configuration Variables::. 189 190 For ‘gitlab’ and ‘bitbucket’ forges similar variables are available: 191 192 • ‘gitlab.user’ specifies the <https://gitlab.com> user. 193 • ‘gitlab.HOST.user’ specifies the user for the HOST ‘gitlab’ 194 instance. 195 • ‘gitlab.host’ specifies the ‘gitlab’ host, unless the HOST argument 196 is non-nil 197 • ‘bitbucket.user’ specifies the <https://bitbucket.org> user. 198 • ‘bitbucket.HOST.user’ specifies the user for the HOST ‘bitbucket’ 199 instance. 200 • ‘bitbucket.host’ specifies the ‘bitbucket’ host, unless the HOST 201 argument is non-nil. 202 203 For the ‘gitea’ and ‘gogs’ forges some similar variables are 204 available, however for some of the ‘ghub.*’ variables no equivalent 205 variable exist for these two forges: 206 207 • ‘gitea.user’ is *not* used because no canonical ‘gitea’ instance 208 exists. 209 • ‘gitea.HOST.user’ specifies the user for the HOST ‘gitea’ instance. 210 • ‘gitea.host’ specifies the ‘gitea’ host, unless the HOST argument 211 is non-nil 212 • ‘gogs.user’ is *not* used because no canonical ‘gogs’ instance 213 exists. 214 • ‘gogs.HOST.user’ specifies the user for the HOST ‘gogs’ instance. 215 • ‘gogs.host’ specifies the ‘gogs’ host, unless the HOST argument is 216 non-nil 217 218 219 File: ghub.info, Node: Setting the Username, Next: Creating and Storing a Token, Prev: Basic Concepts Arguments and Variables, Up: Getting Started 220 221 2.2 Setting the Username 222 ======================== 223 224 Ghub needs to know your username that you use on the host that you want 225 it to connect to. For each host a different Git variable has to be set 226 to specify the username on that host. More than one variable is needed 227 because you might use different usernames on different hosts. 228 229 Setting your Github.com Username 230 -------------------------------- 231 232 To inform Ghub about your "github.com" username do this: 233 234 git config --global github.user USERNAME 235 236 If you need to identify as another user in a particular repository, 237 then you have to set that variable locally: 238 239 cd /path/to/repo 240 git config --local github.user USERNAME 241 242 Setting your Gitlab.com Username 243 -------------------------------- 244 245 To inform Ghub about your "gitlab.com" username do this: 246 247 git config --global gitlab.user USERNAME 248 249 If you need to identify as another user in a particular repository, 250 then you have to set that variable locally: 251 252 cd /path/to/repo 253 git config --local gitlab.user USERNAME 254 255 Make sure you use the correct USERNAME for this forge/host. It might 256 not be the same as on "github.com"! 257 258 Setting your Github Enterprise Username 259 --------------------------------------- 260 261 For Github Enterprise instances you have to specify where the API can be 262 accessed and a different variable has to be used to set the username. 263 264 For example if the API is available at ‘https://example.com/api/v3’, 265 then you should do this: 266 267 git config --global github.example.com/api/v3.user USERNAME 268 269 Make sure you use the correct USERNAME for this instance. It might 270 not be the same as on "github.com"! 271 272 Doing this only tells Ghub who you are on this host, additionally you 273 have to tell Ghub which repository are connected to that forge/host, 274 like so: 275 276 cd /path/to/repo 277 git config --local github.host example.com/api/v3 278 279 Setting your Username for Other Hosts and/or Forges 280 --------------------------------------------------- 281 282 To inform Ghub about your username on HOST (a FORGE instance) do this: 283 284 git config --global FORGE.HOST.user USERNAME 285 286 FORGE can be one of ‘bitbucket’, ‘gitea’ or ‘gogs’. It can also be 287 ‘github’ or ‘gitlab’; but if that is the case, then you should look at 288 the preceding sections instead, which discuss these cases specifically. 289 290 HOST identifies the instance. This actually points at the top-level 291 endpoint of the API and may contain path components, e.g.: 292 ‘example.com/api’. 293 294 If you need to identify as another user in a particular repository, 295 then you have to set that variable locally: 296 297 cd /path/to/repo 298 git config --local FORGE.HOST.user USERNAME 299 300 301 File: ghub.info, Node: Creating and Storing a Token, Next: Github Configuration Variables, Prev: Setting the Username, Up: Getting Started 302 303 2.3 Creating and Storing a Token 304 ================================ 305 306 * Menu: 307 308 * Creating a Token:: 309 * Storing a Token:: 310 311 312 File: ghub.info, Node: Creating a Token, Next: Storing a Token, Up: Creating and Storing a Token 313 314 2.3.1 Creating a Token 315 ---------------------- 316 317 To create a token, use the web interface of the forge/host you want to 318 connect to. Here is a list of pages to do this for certain popular 319 hosts: 320 321 • <https://github.com/settings/tokens> 322 • <https://gitlab.com/-/profile/personal_access_tokens> 323 324 For other forges we cannot provide a functioning URL because they 325 contain unknown values such as your name. Just go to the general 326 settings page of the respective host and then go from there. 327 328 Except on ‘gitea’ and ‘gogs’ each token can be limited to certain 329 "scopes", i.e., it is possible to limit for which purposes any given 330 token can be used. 331 332 Before you create a token to be used for a certain package, you 333 should consult the documentation of that package, which in turn should 334 tell you which scopes are needed and why. The Forge package for example 335 does so in *note (forge)Token Creation::. 336 337 338 File: ghub.info, Node: Storing a Token, Prev: Creating a Token, Up: Creating and Storing a Token 339 340 2.3.2 Storing a Token 341 --------------------- 342 343 Please also see *note (auth)Top:: for all the gory details about 344 Auth-Source. 345 346 The variable ‘auth-sources’ controls how and where Auth-Source keeps 347 its secrets. The default value is a list of three files: 348 ‘("~/.authinfo" "~/.authinfo.gpg" "~/.netrc")’, but to avoid confusion 349 you should make sure that only one of these files exists and then you 350 should also adjust the value of the variable to only ever use that file, 351 for example: 352 353 (setq auth-sources '("~/.authinfo")) 354 355 In ‘~/.authinfo’ secrets are stored in plain text. If you don’t want 356 that, then you should use the encrypted ‘~/.authinfo.gpg’ instead: 357 358 (setq auth-sources '("~/.authinfo.gpg")) 359 360 Auth-Source also supports storing secrets in various external 361 key-chains. See *note (auth)Top:: for more information. 362 363 The default Auth-Source backends only support storing three values 364 per entry; the "machine", the "login" and the "password". Because Ghub 365 uses separate tokens for each package, it has to squeeze four values 366 into those three slots, and it does that by using "USERNAME^PACKAGE" as 367 the "login". 368 369 Assuming your *Github* username is "ziggy", the package is named 370 "forge", and you want to access *Github.com* with the *token* 371 "012345abcdef...", an entry in one of the three mentioned files would 372 then look like this: 373 374 machine api.github.com login ziggy^forge password 012345abcdef... 375 376 Assuming your *Gitlab* username is "ziggy", the package is named 377 "forge", and you want to access *Gitlab.com* with the *token* 378 "012345abcdef...", an entry in one of the three mentioned files would 379 then look like this: 380 381 machine gitlab.com/api/v4 login ziggy^forge password 012345abcdef... 382 383 384 File: ghub.info, Node: Github Configuration Variables, Prev: Creating and Storing a Token, Up: Getting Started 385 386 2.4 Github Configuration Variables 387 ================================== 388 389 The username and, unless you only use Github.com itself, the Github 390 Enterprise instance have to be configured using Git variables. In rare 391 cases it might also be necessary to specify the identity of the local 392 machine, which is done using a lisp variable. 393 394 -- Variable: github.user 395 The Github.com username. This should be set globally and if you 396 have multiple Github.com user accounts, then you should set this 397 locally only for those repositories that you want to access using 398 the secondary identity. 399 400 -- Variable: github.HOST.user 401 This variable serves the same purpose as ‘github.user’ but for the 402 Github Enterprise instance identified by ‘HOST’. 403 404 The reason why separate variables are used is that this makes it 405 possible to set both values globally instead of having to set one 406 of the values locally in each and every repository that is 407 connected to the Github Enterprise instance, not Github.com. 408 409 -- Variable: github.host 410 This variable should only be set locally for a repository and 411 specifies the Github Enterprise edition that that repository is 412 connected to. You should not set this globally because then each 413 and every repository becomes connected to the specified Github 414 Enterprise instance, including those that should actually be 415 connected to Github.com. 416 417 When this is undefined, then "api.github.com" is used (defined in 418 the constant ‘ghub-default-host’, which you should never attempt to 419 change.) 420 421 422 File: ghub.info, Node: API, Next: Notes, Prev: Getting Started, Up: Top 423 424 3 API 425 ***** 426 427 * Menu: 428 429 * Their APIs:: 430 * Making REST Requests:: 431 * Making GraphQL Requests:: 432 * Github Convenience Wrappers:: 433 * Non-Github Convenience Wrappers:: 434 435 436 File: ghub.info, Node: Their APIs, Next: Making REST Requests, Up: API 437 438 3.1 Their APIs 439 ============== 440 441 Of course this manual does not cover the APIs of all forges that it 442 supports, but for your convenience, here are the links to their API 443 manuals: 444 445 • Github: 446 • <https://developer.github.com/v4> (GraphQl) 447 • <https://developer.github.com/v3> (REST) 448 • Gitlab: 449 • <https://docs.gitlab.com/ee/api/README.html> 450 • Gitea: 451 • <https://docs.gitea.io/en-us/api-usage> 452 • <https://try.gitea.io/api/swagger> 453 • Gogs: 454 • <https://github.com/gogs/go-gogs-client/wiki> 455 • Bitbucket: 456 • <https://developer.atlassian.com/bitbucket/api/2/reference> 457 458 459 File: ghub.info, Node: Making REST Requests, Next: Making GraphQL Requests, Prev: Their APIs, Up: API 460 461 3.2 Making REST Requests 462 ======================== 463 464 -- Function: ghub-request method resource &optional params &key query 465 payload headers unpaginate noerror reader username auth host 466 callback errorback url value error extra method* 467 This function makes a request for ‘RESOURCE’ using ‘METHOD’. 468 ‘PARAMS’, ‘QUERY’, ‘PAYLOAD’ and/or ‘HEADERS’ are alists holding 469 additional request data. The response body is returned and the 470 response headers are stored in the variable 471 ‘ghub-response-headers’. 472 473 • ‘METHOD’ is the HTTP method, given as a string. 474 • ‘RESOURCE’ is the resource to access, given as a string 475 beginning with a slash. 476 477 • ‘PARAMS’, ‘QUERY’, ‘PAYLOAD’ and ‘HEADERS’ are alists and are 478 used to specify request data. All these arguments are alists 479 that resemble the JSON expected and returned by the Github 480 API. The keys are symbols and the values stored in the ‘cdr’ 481 (not the ‘cadr’) can be strings, integers, or lists of strings 482 and integers. 483 484 The Github API documentation is vague on how data has to be 485 transmitted and for a particular resource usually just talks 486 about "parameters". Generally speaking when the ‘METHOD’ is 487 "HEAD" or "GET", then they have to be transmitted as a query, 488 otherwise as a payload. 489 490 • Use ‘PARAMS’ to automatically transmit like ‘QUERY’ or 491 ‘PAYLOAD’ would depending on ‘METHOD’. 492 • Use ‘QUERY’ to explicitly transmit data as a query. 493 • Use ‘PAYLOAD’ to explicitly transmit data as a payload. 494 Instead of an alist, ‘PAYLOAD’ may also be a string, in 495 which case it gets encoded as UTF-8 but is otherwise 496 transmitted as-is. 497 • Use ‘HEADERS’ for those rare resources that require that 498 the data is transmitted as headers instead of as a query 499 or payload. When that is the case, then the Github API 500 documentation usually mentions it explicitly. 501 502 • If ‘SILENT’ is non-nil, then progress reports and the like are 503 not messaged. 504 505 • If ‘UNPAGINATE’ is t, then this function makes as many 506 requests as necessary to get all values. If ‘UNPAGINATE’ is a 507 natural number, then it gets at most that many pages. For any 508 other non-nil value it raises an error. 509 510 • If ‘NOERROR’ is non-nil, then no error is raised if the 511 request fails and ‘nil’ is returned instead. If ‘NOERROR’ is 512 ‘return’, then the error payload is returned instead of ‘nil’. 513 514 • If ‘READER’ is non-nil, then it is used to read and return 515 from the response buffer. The default is 516 ‘ghub--read-json-payload’. For the very few resources that do 517 not return JSON, you might want to use ‘ghub--decode-payload’. 518 519 • If ‘USERNAME’ is non-nil, then the request is made on behalf 520 of that user. It is better to specify the user using the Git 521 variable ‘github.user’ for "api.github.com", or 522 ‘github.HOST.user’ if connecting to a Github Enterprise 523 instance. 524 525 • Each package that uses Ghub should use its own token. If 526 ‘AUTH’ is ‘nil’ or unspecified, then the generic ‘ghub’ token 527 is used instead. This is only acceptable for personal 528 utilities. A package that is distributed to other users 529 should always use this argument to identify itself, using a 530 symbol matching its name. 531 532 Package authors who find this inconvenient should write a 533 wrapper around this function and possibly for the 534 method-specific functions as well. 535 536 Beside ‘nil’, some other symbols have a special meaning too. 537 ‘none’ means to make an unauthorized request. ‘basic’ means 538 to make a password based request. If the value is a string, 539 then it is assumed to be a valid token. ‘basic’ and an 540 explicit token string are only intended for internal and 541 debugging uses. 542 543 If ‘AUTH’ is a package symbol, then the scopes are specified 544 using the variable ‘AUTH-github-token-scopes’. It is an error 545 if that is not specified. See ‘ghub-github-token-scopes’ for 546 an example. 547 548 • If ‘HOST’ is non-nil, then connect to that Github instance. 549 This defaults to "api.github.com". When a repository is 550 connected to a Github Enterprise instance, then it is better 551 to specify that using the Git variable ‘github.host’ instead 552 of using this argument. 553 554 • If ‘FORGE’ is ‘gitlab’, then connect to Gitlab.com or, 555 depending on ‘HOST’, to another Gitlab instance. This is only 556 intended for internal use. Instead of using this argument you 557 should use function ‘glab-request’ and other ‘glab-*’ 558 functions. 559 560 • If ‘CALLBACK’ and/or ‘ERRORBACK’ is non-nil, then this 561 function makes one or more asynchronous requests and calls 562 ‘CALLBACK’ or ‘ERRORBACK’ when finished. If no error 563 occurred, then it calls ‘CALLBACK’, unless that is ‘nil’. 564 565 If an error occurred, then it calls ‘ERRORBACK’, or if that is 566 nil, then ‘CALLBACK’. ‘ERRORBACK’ can also be ‘t’, in which 567 case it signals instead. ‘NOERROR’ is ignored for all 568 asynchronous requests. 569 570 Both callbacks are called with four arguments. 571 572 1. For ‘CALLBACK’, the combined value of the retrieved 573 pages. For ‘ERRORBACK’, the error that occurred when 574 retrieving the last page. 575 2. The headers of the last page as an alist. 576 3. Status information provided by ‘url-retrieve’. Its 577 ‘:error’ property holds the same information as the first 578 argument to ‘ERRORBACK’. 579 4. A ‘ghub--req’ struct, which can be passed to 580 ‘ghub-continue’ (which see) to retrieve the next page, if 581 any. 582 583 -- Function: ghub-continue args 584 If there is a next page, then this function retrieves that. 585 586 This function is only intended to be called from callbacks. If 587 there is a next page, then that is retrieved and the buffer that 588 the result will be loaded into is returned, or t if the process has 589 already completed. If there is no next page, then return nil. 590 591 Callbacks are called with four arguments (see ‘ghub-request’). The 592 forth argument is a ‘ghub--req’ struct, intended to be passed to 593 this function. A callback may use the struct’s ‘extra’ slot to 594 pass additional information to the callback that will be called 595 after the next request. Use the function ‘ghub-req-extra’ to get 596 and set the value of that slot. 597 598 As an example, using ‘ghub-continue’ in a callback like so: 599 600 (ghub-get "/users/tarsius/repos" nil 601 :callback (lambda (value _headers _status req) 602 (unless (ghub-continue req) 603 (setq my-value value)))) 604 605 is equivalent to: 606 607 (ghub-get "/users/tarsius/repos" nil 608 :unpaginate t 609 :callback (lambda (value _headers _status _req) 610 (setq my-value value))) 611 612 To demonstrate how to pass information from one callback to the 613 next, here we record when we start fetching each page: 614 615 (ghub-get "/users/tarsius/repos" nil 616 :extra (list (current-time)) 617 :callback (lambda (value _headers _status req) 618 (push (current-time) (ghub-req-extra req)) 619 (unless (ghub-continue req) 620 (setq my-times (ghub-req-extra req)) 621 (setq my-value value)))) 622 623 -- Variable: ghub-response-headers 624 A select few Github API resources respond by transmitting data in 625 the response header instead of in the response body. Because there 626 are so few of these inconsistencies, ‘ghub-request’ always returns 627 the response body. 628 629 To access the response headers use this variable after 630 ‘ghub-request’ has returned. 631 632 -- Function: ghub-response-link-relations req headers payload 633 This function returns an alist of the link relations in ‘HEADERS’, 634 or if optional ‘HEADERS’ is nil, then those in 635 ‘ghub-response-headers’. 636 637 When accessing a Bitbucket instance then the link relations are in 638 ‘PAYLOAD’ instead of ‘HEADERS’, making their API merely RESTish and 639 forcing this function to append those relations to the value of 640 ‘ghub-response-headers’, for later use when this function is called 641 with ‘nil’ for ‘PAYLOAD’. 642 643 644 File: ghub.info, Node: Making GraphQL Requests, Next: Github Convenience Wrappers, Prev: Making REST Requests, Up: API 645 646 3.3 Making GraphQL Requests 647 =========================== 648 649 -- Function: ghub-graphql graphql &optional variables &key username 650 auth host callback silent callback errorback value extra 651 This function makes a GraphQL request using ‘GRAPHQL’ and 652 ‘VARIABLES’ as inputs. ‘GRAPHQL’ is a GraphQL string. ‘VARIABLES’ 653 is a JSON-like alist. The other arguments behave as for 654 ‘ghub-request’ (which see). 655 656 The response is returned as a JSON-like alist. Even if the 657 response contains ‘errors’, this function does not raise an error. 658 Cursor-handling is likewise left to the caller. 659 660 ‘ghub-graphql’ is a thin convenience wrapper around ‘ghub-request’, 661 similar to ‘ghub-post’ and friends. While the latter only hard-code the 662 value of the ‘METHOD’ argument, the former also hard-codes ‘RESOURCE’ 663 and constructs ‘PAYLOAD’ from ‘GRAPHQL’ and ‘VARIABLES’. It also drops 664 ‘UNPAGINATE’, ‘NOERROR’, ‘READER’ (internal functions expect alist-ified 665 JSON) and ‘FORGE’ (only Github currently supports GraphQL). 666 667 ‘ghub-graphql’ does not account for the fact that pagination works 668 differently in GraphQL than it does in REST, so users of this function 669 have to deal with that themselves. Likewise error handling works 670 differently and has to be done by the caller too. 671 672 An early attempt at implementing automatic unpaginating for GraphQL 673 can be found in the ‘faithful-graphql’ branch, provided I haven’t 674 deleted that by now. On that branch I try to do things as intended by 675 the designers of GraphQL, using variables and fragments, and drowning in 676 a sea of boilerplate. 677 678 The problem with that approach is that it only works for applications 679 that fetch specific information on demand and actually want things to be 680 paginated. I am convinced that GraphQL is very nice for web apps. 681 682 However the Forge package for which I have implemented all of this 683 has very different needs. It wants to fetch "all the data" and "cache" 684 it locally, so that it is available even when there is no internet 685 connection. GraphQL was designed around the idea that you should be 686 able to "ask for what you need and get exactly that". But when that 687 boils down to "look, if I persist, then you are going to hand me over 688 all the data anyway, so just caught it up already", then things start to 689 fall apart. If Github’s GraphQL allowed pagination to be turned off 690 completely, then teaching ‘ghub-graphql’ about error handling would be 691 enough. 692 693 But it doesn’t and when doing things as intended, then that leads to 694 huge amounts of repetitive boilerplate, which is so boring to write that 695 doing it without introducing bugs left and right is near impossible; so 696 I decided to give up on GraphQL variables, fragments and conditions, and 697 instead implement something more powerful, though also more opinionated. 698 699 -- Function: ghub--graphql-vacuum query variables callback &optional 700 until &key narrow username auth host forge 701 This function is an opinionated alternative to ‘ghub-graphql’. It 702 relies on dark magic to get the job done. 703 704 It makes an initial request using ‘QUERY’. It then looks for 705 paginated edges in the returned data and makes more requests to 706 resolve them. In order to do so it automatically transforms the 707 initial ‘QUERY’ into another query suitable for that particular 708 edge. The data retrieved by subsequent requests is then injected 709 into the data of the original request before that is returned or 710 passed to the callback. If subsequently retrieved data features 711 new paginated edges, then those are followed recursively. 712 713 The end result is essentially the same as using ‘ghub-graphql’, if 714 only it were possible to say "do not paginate anything". The 715 implementation is much more complicated because it is not possible 716 to do that. 717 718 ‘QUERY’ is a GraphQL query expressed as an s-expression. The 719 bundled ‘gsexp’ library is used to turn that into a GraphQL query 720 string. Only a subset of the GraphQL features are supported; 721 fragments for example are not, and magical stuff happens to 722 variables. This is not documented yet, I am afraid. Look at 723 existing callers. 724 725 ‘VARIABLES’ is a JSON-like alist as for ‘ghub-graphql’. 726 727 ‘UNTIL’ is an alist ‘((EDGE-until . VALUE)...)’. When unpaginating 728 ‘EDGE’ try not to fetch beyond the element whose first field has 729 the value ‘VALUE’ and remove that element as well as all "lesser" 730 elements from the retrieved data if necessary. Look at 731 ‘forge--pull-repository’ for an example. This is only useful if 732 you "cache" the response locally and want to avoid fetching data 733 again that you already have. 734 735 Other arguments behave as for ‘ghub-graphql’ and ‘ghub-request’, 736 more or less. 737 738 Using ‘ghub--graphql-vacuum’, the following resource specific 739 functions are implemented. These functions are not part of the public 740 API yet and are very much subject to change. 741 742 -- Function: ghub-fetch-repository owner name callback &optional until 743 &key username auth host forge 744 This function asynchronously fetches forge data about the specified 745 repository. Once all data has been collected, ‘CALLBACK’ is called 746 with the data as the only argument. 747 748 -- Function: ghub-fetch-issue owner name callback &optional until &key 749 username auth host forge 750 This function asynchronously fetches forge data about the specified 751 issue. Once all data has been collected, ‘CALLBACK’ is called with 752 the data as the only argument. 753 754 -- Function: ghub-fetch-pullreq owner name callback &optional until 755 &key username auth host forge 756 This function asynchronously fetches forge data about the specified 757 pull-request. Once all data has been collected, ‘CALLBACK’ is 758 called with the data as the only argument. 759 760 Note that in order to avoid duplication all of these functions base 761 their initial query on the query stored in ‘ghub-fetch-repository’. The 762 latter two pass that query through ‘ghub--graphql-prepare-query’, which 763 then uses ‘ghub--graphql-narrow-query’ to remove parts the caller is not 764 interested in. These two functions are also used internally, when 765 unpaginating, but as demonstrated here they can be useful even before 766 making an initial request. 767 768 769 File: ghub.info, Node: Github Convenience Wrappers, Next: Non-Github Convenience Wrappers, Prev: Making GraphQL Requests, Up: API 770 771 3.4 Github Convenience Wrappers 772 =============================== 773 774 -- Function: ghub-head resource &optional params &key query payload 775 headers unpaginate noerror reader username auth host callback 776 errorback 777 -- Function: ghub-get resource &optional params &key query payload 778 headers unpaginate noerror reader username auth host callback 779 errorback 780 These functions are simple wrappers around ‘ghub-request’. Their 781 signature is identical to that of the latter, except that they do 782 not have an argument named ‘METHOD’. The HTTP method is instead 783 given by the second word in the function name. 784 785 As described in the documentation for ‘ghub-request’, it depends on 786 the used method whether the value of the ‘PARAMS’ argument is used 787 as the query or the payload. For the "HEAD" and "GET" methods it 788 is used as the query. 789 790 -- Function: ghub-put resource &optional params &key query payload 791 headers unpaginate noerror reader username auth host callback 792 errorback 793 -- Function: ghub-post resource &optional params &key query payload 794 headers unpaginate noerror reader username auth host callback 795 errorback 796 -- Function: ghub-patch resource &optional params &key query payload 797 headers unpaginate noerror reader username auth host callback 798 errorback 799 -- Function: ghub-delete resource &optional params &key query payload 800 headers unpaginate noerror reader username auth host callback 801 errorback 802 These functions are simple wrappers around ‘ghub-request’. Their 803 signature is identical to that of the latter, except that they do 804 not have an argument named ‘METHOD’. The HTTP method is instead 805 given by the second word in the function name. 806 807 As described in the documentation for ‘ghub-request’, it depends on 808 the used method whether the value of the ‘PARAMS’ argument is used 809 as the query or the payload. For the "PUT", "POST", "PATCH" and 810 "DELETE" methods it is used as the payload. 811 812 -- Function: ghub-wait resource &optional duration &key username auth 813 host 814 Some API requests result in an immediate successful response even 815 when the requested action has not actually been carried out yet. 816 An example is the request for the creation of a new repository, 817 which doesn’t cause the repository to immediately become available. 818 The Github API documentation usually mentions this when describing 819 an affected resource. 820 821 If you want to do something with some resource right after making a 822 request for its creation, then you might have to wait for it to 823 actually be created. This function can be used to do so. It 824 repeatedly tries to access the resource until it becomes available 825 or until a timeout is reached. In the latter case it signals 826 ‘ghub-error’. 827 828 ‘RESOURCE’ specifies the resource that this function waits for. 829 830 ‘DURATION’ specifies the maximum number of seconds to wait for, 831 defaulting to 64 seconds. Emacs will block during that time, but 832 the user can abort using ‘C-g’. 833 834 The first attempt is made immediately and will often succeed. If 835 not, then another attempt is made after two seconds, and each 836 subsequent attempt is made after waiting as long as we already 837 waited between all preceding attempts combined. 838 839 See ‘ghub-request’’s documentation above for information about the 840 other arguments. 841 842 843 File: ghub.info, Node: Non-Github Convenience Wrappers, Prev: Github Convenience Wrappers, Up: API 844 845 3.5 Non-Github Convenience Wrappers 846 =================================== 847 848 ‘ghub-request’ and ‘ghub-METHOD’ can be used to make a request for any 849 of the supported forge types, but except when making a request for a 850 ‘github’ instance, then that requires the use of the FORGE argument. 851 852 To avoid that, functions named ‘FORGE-request’ and ‘FORGE-METHOD’ are 853 also available. The following forms are equivalent, for example: 854 855 (ghub-get ... :auth 'PACKAGE :forge 'gitlab) 856 (glab-get ... :auth 'PACKAGE) 857 858 These forms would remain equivalent even if you did not specify a 859 value for the AUTH arguments — but you should not do that if you plan to 860 share your code with others (see *note Using Ghub in Your Own 861 Package::). If you do omit AUTH, then the request is made on behalf of 862 the ‘ghub’ package, *regardless* of the symbol prefix of the function 863 you use to do so. 864 865 All ‘FORGE-request’ and ‘FORGE-METHOD’ functions, including but not 866 limited to ‘ghub-METHOD’, are very simple wrappers around 867 ‘ghub-request’. They take fewer arguments than ‘ghub-request’ and 868 instead pass constant values for the arguments METHOD and/or FORGE. 869 870 -- Function: buck-request resource &optional params &key query payload 871 headers unpaginate noerror reader username auth host callback 872 errorback 873 -- Function: glab-request resource &optional params &key query payload 874 headers unpaginate noerror reader username auth host callback 875 errorback 876 -- Function: gogs-request resource &optional params &key query payload 877 headers unpaginate noerror reader username auth host callback 878 errorback 879 Wrappers around ‘ghub-request’ which hardcode the FORGE to either 880 ‘bitbucket’, ‘gitlab’, ‘gogs’ or ‘gitea’. 881 882 -- Function: buck-get resource &optional params &key query payload 883 headers unpaginate noerror reader username auth host callback 884 errorback 885 -- Function: buck-put resource &optional params &key query payload 886 headers unpaginate noerror reader username auth host callback 887 errorback 888 -- Function: buck-post resource &optional params &key query payload 889 headers unpaginate noerror reader username auth host callback 890 errorback 891 -- Function: buck-delete resource &optional params &key query payload 892 headers unpaginate noerror reader username auth host callback 893 errorback 894 -- Function: glab-head resource &optional params &key query payload 895 headers unpaginate noerror reader username auth host callback 896 errorback 897 -- Function: glab-get resource &optional params &key query payload 898 headers unpaginate noerror reader username auth host callback 899 errorback 900 -- Function: glab-put resource &optional params &key query payload 901 headers unpaginate noerror reader username auth host callback 902 errorback 903 -- Function: glab-post resource &optional params &key query payload 904 headers unpaginate noerror reader username auth host callback 905 errorback 906 -- Function: glab-patch resource &optional params &key query payload 907 headers unpaginate noerror reader username auth host callback 908 errorback 909 -- Function: glab-delete resource &optional params &key query payload 910 headers unpaginate noerror reader username auth host callback 911 errorback 912 -- Function: gogs-get resource &optional params &key query payload 913 headers unpaginate noerror reader username auth host callback 914 errorback 915 -- Function: gogs-put resource &optional params &key query payload 916 headers unpaginate noerror reader username auth host callback 917 errorback 918 -- Function: gogs-post resource &optional params &key query payload 919 headers unpaginate noerror reader username auth host callback 920 errorback 921 -- Function: gogs-patch resource &optional params &key query payload 922 headers unpaginate noerror reader username auth host callback 923 errorback 924 -- Function: gogs-delete resource &optional params &key query payload 925 headers unpaginate noerror reader username auth host callback 926 errorback 927 -- Function: gtea-get resource &optional params &key query payload 928 headers unpaginate noerror reader username auth host callback 929 errorback 930 -- Function: gtea-put resource &optional params &key query payload 931 headers unpaginate noerror reader username auth host callback 932 errorback 933 -- Function: gtea-post resource &optional params &key query payload 934 headers unpaginate noerror reader username auth host callback 935 errorback 936 -- Function: gtea-patch resource &optional params &key query payload 937 headers unpaginate noerror reader username auth host callback 938 errorback 939 -- Function: gtea-delete resource &optional params &key query payload 940 headers unpaginate noerror reader username auth host callback 941 errorback 942 Wrappers around ‘FORGE-METHOD’ which hardcode the FORGE to either 943 ‘bitbucket’, ‘gitlab’, ‘gogs’ or ‘gitea’, and the METHOD to the 944 implied method. 945 946 Note that ‘buck-head’, ‘buck-patch’, ‘gogs-head’ and ‘gtea-head’ do 947 not exist because the respective APIs do not appear to support 948 these methods. 949 950 951 File: ghub.info, Node: Notes, Next: Function Index, Prev: API, Up: Top 952 953 4 Notes 954 ******* 955 956 * Menu: 957 958 * Using Ghub in Personal Scripts:: 959 * Using Ghub in Your Own Package:: 960 * Forge Limitations and Notes:: 961 962 963 File: ghub.info, Node: Using Ghub in Personal Scripts, Next: Using Ghub in Your Own Package, Up: Notes 964 965 4.1 Using Ghub in Personal Scripts 966 ================================== 967 968 You can of course use ‘ghub-request’ and its wrapper functions in your 969 personal scripts. Unlike when you use Ghub in a package that you 970 distribute for others to use, you don’t have to explicitly specify a 971 package in personal scripts. 972 973 ;; This is perfectly acceptable in personal scripts ... 974 (ghub-get "/user") 975 976 ;; ... and actually equals to 977 (ghub-get "/user" nil :auth 'ghub) 978 979 ;; In packages you have to specify the package using AUTH. 980 (ghub-get "/user" nil :auth 'some-package) 981 982 When the ‘AUTH’ argument is not specified, then a request is made on 983 behalf of the ‘ghub’ package itself. Like for any other package you 984 have to create a dedicated token of coures. 985 986 987 File: ghub.info, Node: Using Ghub in Your Own Package, Next: Forge Limitations and Notes, Prev: Using Ghub in Personal Scripts, Up: Notes 988 989 4.2 Using Ghub in Your Own Package 990 ================================== 991 992 Every package should use its own token. This allows you as the author 993 of some package to only request access to API scopes that are actually 994 needed, which in turn might make it easier for users to trust your 995 package not to do unwanted things. 996 997 You have to tell ‘ghub-request’ on behalf of which package a request 998 is being made by passing the symbol ‘PACKAGE’ as the value of its ‘AUTH’ 999 argument. 1000 1001 (ghub-request "GET" "/user" nil :auth 'PACKAGE) 1002 1003 Keep in mind that the users of your package will have to manually 1004 create a suitable token. To make that easier, you should not only link 1005 to this manual but also prominently mention the scopes the token needs; 1006 and explain what they are needed for. 1007 1008 1009 File: ghub.info, Node: Forge Limitations and Notes, Prev: Using Ghub in Your Own Package, Up: Notes 1010 1011 4.3 Forge Limitations and Notes 1012 =============================== 1013 1014 • There are no default Gitea and Gogs instances so the variables 1015 ‘gitea.host’ and ‘gogs.host’ are not taken into account. 1016 1017 • Gitea and Gogs do not support limiting a token to certain scopes. 1018 1019 • The Bitbucket API is fairly broken and my willingness to deal with 1020 that is extremely limited unless someone pays me vast amounts of 1021 money. 1022 1023 • The Gitlab API documentation is not always accurate, though I don’t 1024 have an example at hand. It also isn’t structured well, making it 1025 occasionally difficult to find the information one is looking for. 1026 1027 • Where one would use ‘user/repo’ when accessing another forge, one 1028 has to use ‘user%2Frepo’ when accessing Gitlab, e.g.: 1029 1030 (glab-get "/projects/python-mode-devs%2Fpython-mode") 1031 1032 1033 File: ghub.info, Node: Function Index, Next: Variable Index, Prev: Notes, Up: Top 1034 1035 Appendix A Function Index 1036 ************************* 1037 1038 1039 * Menu: 1040 1041 * buck-delete: Non-Github Convenience Wrappers. 1042 (line 49) 1043 * buck-get: Non-Github Convenience Wrappers. 1044 (line 40) 1045 * buck-post: Non-Github Convenience Wrappers. 1046 (line 46) 1047 * buck-put: Non-Github Convenience Wrappers. 1048 (line 43) 1049 * buck-request: Non-Github Convenience Wrappers. 1050 (line 28) 1051 * ghub--graphql-vacuum: Making GraphQL Requests. 1052 (line 56) 1053 * ghub-continue: Making REST Requests. 1054 (line 125) 1055 * ghub-delete: Github Convenience Wrappers. 1056 (line 31) 1057 * ghub-fetch-issue: Making GraphQL Requests. 1058 (line 105) 1059 * ghub-fetch-pullreq: Making GraphQL Requests. 1060 (line 111) 1061 * ghub-fetch-repository: Making GraphQL Requests. 1062 (line 99) 1063 * ghub-get: Github Convenience Wrappers. 1064 (line 9) 1065 * ghub-graphql: Making GraphQL Requests. 1066 (line 6) 1067 * ghub-head: Github Convenience Wrappers. 1068 (line 6) 1069 * ghub-patch: Github Convenience Wrappers. 1070 (line 28) 1071 * ghub-post: Github Convenience Wrappers. 1072 (line 25) 1073 * ghub-put: Github Convenience Wrappers. 1074 (line 22) 1075 * ghub-request: Making REST Requests. 1076 (line 6) 1077 * ghub-response-link-relations: Making REST Requests. 1078 (line 174) 1079 * ghub-wait: Github Convenience Wrappers. 1080 (line 44) 1081 * glab-delete: Non-Github Convenience Wrappers. 1082 (line 67) 1083 * glab-get: Non-Github Convenience Wrappers. 1084 (line 55) 1085 * glab-head: Non-Github Convenience Wrappers. 1086 (line 52) 1087 * glab-patch: Non-Github Convenience Wrappers. 1088 (line 64) 1089 * glab-post: Non-Github Convenience Wrappers. 1090 (line 61) 1091 * glab-put: Non-Github Convenience Wrappers. 1092 (line 58) 1093 * glab-request: Non-Github Convenience Wrappers. 1094 (line 31) 1095 * gogs-delete: Non-Github Convenience Wrappers. 1096 (line 82) 1097 * gogs-get: Non-Github Convenience Wrappers. 1098 (line 70) 1099 * gogs-patch: Non-Github Convenience Wrappers. 1100 (line 79) 1101 * gogs-post: Non-Github Convenience Wrappers. 1102 (line 76) 1103 * gogs-put: Non-Github Convenience Wrappers. 1104 (line 73) 1105 * gogs-request: Non-Github Convenience Wrappers. 1106 (line 34) 1107 * gtea-delete: Non-Github Convenience Wrappers. 1108 (line 97) 1109 * gtea-get: Non-Github Convenience Wrappers. 1110 (line 85) 1111 * gtea-patch: Non-Github Convenience Wrappers. 1112 (line 94) 1113 * gtea-post: Non-Github Convenience Wrappers. 1114 (line 91) 1115 * gtea-put: Non-Github Convenience Wrappers. 1116 (line 88) 1117 1118 1119 File: ghub.info, Node: Variable Index, Prev: Function Index, Up: Top 1120 1121 Appendix B Variable Index 1122 ************************* 1123 1124 1125 * Menu: 1126 1127 * ghub-response-headers: Making REST Requests. 1128 (line 165) 1129 * github.host: Github Configuration Variables. 1130 (line 26) 1131 * github.HOST.user: Github Configuration Variables. 1132 (line 17) 1133 * github.user: Github Configuration Variables. 1134 (line 11) 1135 1136 1137 1138 Tag Table: 1139 Node: Top764 1140 Node: Introduction2288 1141 Node: Getting Started2693 1142 Node: Basic Concepts Arguments and Variables4375 1143 Node: Setting the Username8229 1144 Ref: Setting your Githubcom Username8713 1145 Ref: Setting your Gitlabcom Username9069 1146 Ref: Setting your Github Enterprise Username9535 1147 Ref: Setting your Username for Other Hosts and/or Forges10266 1148 Node: Creating and Storing a Token11072 1149 Node: Creating a Token11336 1150 Node: Storing a Token12356 1151 Node: Github Configuration Variables14201 1152 Node: API15912 1153 Node: Their APIs16150 1154 Node: Making REST Requests16875 1155 Node: Making GraphQL Requests26288 1156 Node: Github Convenience Wrappers32917 1157 Node: Non-Github Convenience Wrappers36621 1158 Node: Notes42125 1159 Node: Using Ghub in Personal Scripts42332 1160 Node: Using Ghub in Your Own Package43223 1161 Node: Forge Limitations and Notes44158 1162 Node: Function Index45131 1163 Node: Variable Index50780 1164 1165 End Tag Table 1166 1167 1168 Local Variables: 1169 coding: utf-8 1170 End: