index.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. const VERSION = "1.1.2";
  4. /**
  5. * Some “list” response that can be paginated have a different response structure
  6. *
  7. * They have a `total_count` key in the response (search also has `incomplete_results`,
  8. * /installation/repositories also has `repository_selection`), as well as a key with
  9. * the list of the items which name varies from endpoint to endpoint:
  10. *
  11. * - https://developer.github.com/v3/search/#example (key `items`)
  12. * - https://developer.github.com/v3/checks/runs/#response-3 (key: `check_runs`)
  13. * - https://developer.github.com/v3/checks/suites/#response-1 (key: `check_suites`)
  14. * - https://developer.github.com/v3/apps/installations/#list-repositories (key: `repositories`)
  15. * - https://developer.github.com/v3/apps/installations/#list-installations-for-a-user (key `installations`)
  16. *
  17. * Octokit normalizes these responses so that paginated results are always returned following
  18. * the same structure. One challenge is that if the list response has only one page, no Link
  19. * header is provided, so this header alone is not sufficient to check wether a response is
  20. * paginated or not. For the exceptions with the namespace, a fallback check for the route
  21. * paths has to be added in order to normalize the response. We cannot check for the total_count
  22. * property because it also exists in the response of Get the combined status for a specific ref.
  23. */
  24. const REGEX = [/^\/search\//, /^\/repos\/[^/]+\/[^/]+\/commits\/[^/]+\/(check-runs|check-suites)([^/]|$)/, /^\/installation\/repositories([^/]|$)/, /^\/user\/installations([^/]|$)/, /^\/repos\/[^/]+\/[^/]+\/actions\/secrets([^/]|$)/, /^\/repos\/[^/]+\/[^/]+\/actions\/workflows(\/[^/]+\/runs)?([^/]|$)/, /^\/repos\/[^/]+\/[^/]+\/actions\/runs(\/[^/]+\/(artifacts|jobs))?([^/]|$)/];
  25. function normalizePaginatedListResponse(octokit, url, response) {
  26. const path = url.replace(octokit.request.endpoint.DEFAULTS.baseUrl, "");
  27. const responseNeedsNormalization = REGEX.find(regex => regex.test(path));
  28. if (!responseNeedsNormalization) return; // keep the additional properties intact as there is currently no other way
  29. // to retrieve the same information.
  30. const incompleteResults = response.data.incomplete_results;
  31. const repositorySelection = response.data.repository_selection;
  32. const totalCount = response.data.total_count;
  33. delete response.data.incomplete_results;
  34. delete response.data.repository_selection;
  35. delete response.data.total_count;
  36. const namespaceKey = Object.keys(response.data)[0];
  37. const data = response.data[namespaceKey];
  38. response.data = data;
  39. if (typeof incompleteResults !== "undefined") {
  40. response.data.incomplete_results = incompleteResults;
  41. }
  42. if (typeof repositorySelection !== "undefined") {
  43. response.data.repository_selection = repositorySelection;
  44. }
  45. response.data.total_count = totalCount;
  46. Object.defineProperty(response.data, namespaceKey, {
  47. get() {
  48. octokit.log.warn(`[@octokit/paginate-rest] "response.data.${namespaceKey}" is deprecated for "GET ${path}". Get the results directly from "response.data"`);
  49. return Array.from(data);
  50. }
  51. });
  52. }
  53. function iterator(octokit, route, parameters) {
  54. const options = octokit.request.endpoint(route, parameters);
  55. const method = options.method;
  56. const headers = options.headers;
  57. let url = options.url;
  58. return {
  59. [Symbol.asyncIterator]: () => ({
  60. next() {
  61. if (!url) {
  62. return Promise.resolve({
  63. done: true
  64. });
  65. }
  66. return octokit.request({
  67. method,
  68. url,
  69. headers
  70. }).then(response => {
  71. normalizePaginatedListResponse(octokit, url, response); // `response.headers.link` format:
  72. // '<https://api.github.com/users/aseemk/followers?page=2>; rel="next", <https://api.github.com/users/aseemk/followers?page=2>; rel="last"'
  73. // sets `url` to undefined if "next" URL is not present or `link` header is not set
  74. url = ((response.headers.link || "").match(/<([^>]+)>;\s*rel="next"/) || [])[1];
  75. return {
  76. value: response
  77. };
  78. });
  79. }
  80. })
  81. };
  82. }
  83. function paginate(octokit, route, parameters, mapFn) {
  84. if (typeof parameters === "function") {
  85. mapFn = parameters;
  86. parameters = undefined;
  87. }
  88. return gather(octokit, [], iterator(octokit, route, parameters)[Symbol.asyncIterator](), mapFn);
  89. }
  90. function gather(octokit, results, iterator, mapFn) {
  91. return iterator.next().then(result => {
  92. if (result.done) {
  93. return results;
  94. }
  95. let earlyExit = false;
  96. function done() {
  97. earlyExit = true;
  98. }
  99. results = results.concat(mapFn ? mapFn(result.value, done) : result.value.data);
  100. if (earlyExit) {
  101. return results;
  102. }
  103. return gather(octokit, results, iterator, mapFn);
  104. });
  105. }
  106. /**
  107. * @param octokit Octokit instance
  108. * @param options Options passed to Octokit constructor
  109. */
  110. function paginateRest(octokit) {
  111. return {
  112. paginate: Object.assign(paginate.bind(null, octokit), {
  113. iterator: iterator.bind(null, octokit)
  114. })
  115. };
  116. }
  117. paginateRest.VERSION = VERSION;
  118. exports.paginateRest = paginateRest;
  119. //# sourceMappingURL=index.js.map