From cbecb84133ccead0a7b53d73adc2eef80b352831 Mon Sep 17 00:00:00 2001 From: "T.J. Corrigan" Date: Tue, 1 Nov 2022 21:49:19 +0000 Subject: [PATCH 1/2] try adding graphql pagination --- dist/index.js | 452 +++++++++++++++++++++++++------------------------- 1 file changed, 228 insertions(+), 224 deletions(-) diff --git a/dist/index.js b/dist/index.js index 833caf192..949f81ca5 100644 --- a/dist/index.js +++ b/dist/index.js @@ -250,7 +250,7 @@ exports.getOctokitOptions = exports.GitHub = exports.defaults = exports.context const Context = __importStar(__webpack_require__(53)); const Utils = __importStar(__webpack_require__(914)); // octokit + plugins -const core_1 = __webpack_require__(762); +const core_1 = __webpack_require__(525); const plugin_rest_endpoint_methods_1 = __webpack_require__(45); const plugin_paginate_rest_1 = __webpack_require__(193); exports.context = new Context.Context(); @@ -10969,6 +10969,190 @@ function getExecOutput(commandLine, args, options) { exports.getExecOutput = getExecOutput; //# sourceMappingURL=exec.js.map +/***/ }), + +/***/ 525: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, '__esModule', { value: true }); + +var universalUserAgent = __webpack_require__(429); +var beforeAfterHook = __webpack_require__(682); +var request = __webpack_require__(234); +var graphql = __webpack_require__(668); +var authToken = __webpack_require__(334); + +function _objectWithoutPropertiesLoose(source, excluded) { + if (source == null) return {}; + var target = {}; + var sourceKeys = Object.keys(source); + var key, i; + + for (i = 0; i < sourceKeys.length; i++) { + key = sourceKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + target[key] = source[key]; + } + + return target; +} + +function _objectWithoutProperties(source, excluded) { + if (source == null) return {}; + + var target = _objectWithoutPropertiesLoose(source, excluded); + + var key, i; + + if (Object.getOwnPropertySymbols) { + var sourceSymbolKeys = Object.getOwnPropertySymbols(source); + + for (i = 0; i < sourceSymbolKeys.length; i++) { + key = sourceSymbolKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; + target[key] = source[key]; + } + } + + return target; +} + +const VERSION = "3.6.0"; + +const _excluded = ["authStrategy"]; +class Octokit { + constructor(options = {}) { + const hook = new beforeAfterHook.Collection(); + const requestDefaults = { + baseUrl: request.request.endpoint.DEFAULTS.baseUrl, + headers: {}, + request: Object.assign({}, options.request, { + // @ts-ignore internal usage only, no need to type + hook: hook.bind(null, "request") + }), + mediaType: { + previews: [], + format: "" + } + }; // prepend default user agent with `options.userAgent` if set + + requestDefaults.headers["user-agent"] = [options.userAgent, `octokit-core.js/${VERSION} ${universalUserAgent.getUserAgent()}`].filter(Boolean).join(" "); + + if (options.baseUrl) { + requestDefaults.baseUrl = options.baseUrl; + } + + if (options.previews) { + requestDefaults.mediaType.previews = options.previews; + } + + if (options.timeZone) { + requestDefaults.headers["time-zone"] = options.timeZone; + } + + this.request = request.request.defaults(requestDefaults); + this.graphql = graphql.withCustomRequest(this.request).defaults(requestDefaults); + this.log = Object.assign({ + debug: () => {}, + info: () => {}, + warn: console.warn.bind(console), + error: console.error.bind(console) + }, options.log); + this.hook = hook; // (1) If neither `options.authStrategy` nor `options.auth` are set, the `octokit` instance + // is unauthenticated. The `this.auth()` method is a no-op and no request hook is registered. + // (2) If only `options.auth` is set, use the default token authentication strategy. + // (3) If `options.authStrategy` is set then use it and pass in `options.auth`. Always pass own request as many strategies accept a custom request instance. + // TODO: type `options.auth` based on `options.authStrategy`. + + if (!options.authStrategy) { + if (!options.auth) { + // (1) + this.auth = async () => ({ + type: "unauthenticated" + }); + } else { + // (2) + const auth = authToken.createTokenAuth(options.auth); // @ts-ignore ¯\_(ツ)_/¯ + + hook.wrap("request", auth.hook); + this.auth = auth; + } + } else { + const { + authStrategy + } = options, + otherOptions = _objectWithoutProperties(options, _excluded); + + const auth = authStrategy(Object.assign({ + request: this.request, + log: this.log, + // we pass the current octokit instance as well as its constructor options + // to allow for authentication strategies that return a new octokit instance + // that shares the same internal state as the current one. The original + // requirement for this was the "event-octokit" authentication strategy + // of https://github.com/probot/octokit-auth-probot. + octokit: this, + octokitOptions: otherOptions + }, options.auth)); // @ts-ignore ¯\_(ツ)_/¯ + + hook.wrap("request", auth.hook); + this.auth = auth; + } // apply plugins + // https://stackoverflow.com/a/16345172 + + + const classConstructor = this.constructor; + classConstructor.plugins.forEach(plugin => { + Object.assign(this, plugin(this, options)); + }); + } + + static defaults(defaults) { + const OctokitWithDefaults = class extends this { + constructor(...args) { + const options = args[0] || {}; + + if (typeof defaults === "function") { + super(defaults(options)); + return; + } + + super(Object.assign({}, defaults, options, options.userAgent && defaults.userAgent ? { + userAgent: `${options.userAgent} ${defaults.userAgent}` + } : null)); + } + + }; + return OctokitWithDefaults; + } + /** + * Attach a plugin (or many) to your Octokit instance. + * + * @example + * const API = Octokit.plugin(plugin1, plugin2, plugin3, ...) + */ + + + static plugin(...newPlugins) { + var _a; + + const currentPlugins = this.plugins; + const NewOctokit = (_a = class extends this {}, _a.plugins = currentPlugins.concat(newPlugins.filter(plugin => !currentPlugins.includes(plugin))), _a); + return NewOctokit; + } + +} +Octokit.VERSION = VERSION; +Octokit.plugins = []; + +exports.Octokit = Octokit; +//# sourceMappingURL=index.js.map + + /***/ }), /***/ 526: @@ -12030,63 +12214,67 @@ function register(state, name, method, options) { /***/ 682: /***/ (function(module, __unusedexports, __webpack_require__) { -var register = __webpack_require__(670) -var addHook = __webpack_require__(549) -var removeHook = __webpack_require__(819) +var register = __webpack_require__(670); +var addHook = __webpack_require__(549); +var removeHook = __webpack_require__(819); // bind with array of arguments: https://stackoverflow.com/a/21792913 -var bind = Function.bind -var bindable = bind.bind(bind) - -function bindApi (hook, state, name) { - var removeHookRef = bindable(removeHook, null).apply(null, name ? [state, name] : [state]) - hook.api = { remove: removeHookRef } - hook.remove = removeHookRef - - ;['before', 'error', 'after', 'wrap'].forEach(function (kind) { - var args = name ? [state, kind, name] : [state, kind] - hook[kind] = hook.api[kind] = bindable(addHook, null).apply(null, args) - }) +var bind = Function.bind; +var bindable = bind.bind(bind); + +function bindApi(hook, state, name) { + var removeHookRef = bindable(removeHook, null).apply( + null, + name ? [state, name] : [state] + ); + hook.api = { remove: removeHookRef }; + hook.remove = removeHookRef; + ["before", "error", "after", "wrap"].forEach(function (kind) { + var args = name ? [state, kind, name] : [state, kind]; + hook[kind] = hook.api[kind] = bindable(addHook, null).apply(null, args); + }); } -function HookSingular () { - var singularHookName = 'h' +function HookSingular() { + var singularHookName = "h"; var singularHookState = { - registry: {} - } - var singularHook = register.bind(null, singularHookState, singularHookName) - bindApi(singularHook, singularHookState, singularHookName) - return singularHook + registry: {}, + }; + var singularHook = register.bind(null, singularHookState, singularHookName); + bindApi(singularHook, singularHookState, singularHookName); + return singularHook; } -function HookCollection () { +function HookCollection() { var state = { - registry: {} - } + registry: {}, + }; - var hook = register.bind(null, state) - bindApi(hook, state) + var hook = register.bind(null, state); + bindApi(hook, state); - return hook + return hook; } -var collectionHookDeprecationMessageDisplayed = false -function Hook () { +var collectionHookDeprecationMessageDisplayed = false; +function Hook() { if (!collectionHookDeprecationMessageDisplayed) { - console.warn('[before-after-hook]: "Hook()" repurposing warning, use "Hook.Collection()". Read more: https://git.io/upgrade-before-after-hook-to-1.4') - collectionHookDeprecationMessageDisplayed = true + console.warn( + '[before-after-hook]: "Hook()" repurposing warning, use "Hook.Collection()". Read more: https://git.io/upgrade-before-after-hook-to-1.4' + ); + collectionHookDeprecationMessageDisplayed = true; } - return HookCollection() + return HookCollection(); } -Hook.Singular = HookSingular.bind() -Hook.Collection = HookCollection.bind() +Hook.Singular = HookSingular.bind(); +Hook.Collection = HookCollection.bind(); -module.exports = Hook +module.exports = Hook; // expose constructors as a named property for TypeScript -module.exports.Hook = Hook -module.exports.Singular = Hook.Singular -module.exports.Collection = Hook.Collection +module.exports.Hook = Hook; +module.exports.Singular = Hook.Singular; +module.exports.Collection = Hook.Collection; /***/ }), @@ -12371,190 +12559,6 @@ function range(a, b, str) { module.exports = require("zlib"); -/***/ }), - -/***/ 762: -/***/ (function(__unusedmodule, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, '__esModule', { value: true }); - -var universalUserAgent = __webpack_require__(429); -var beforeAfterHook = __webpack_require__(682); -var request = __webpack_require__(234); -var graphql = __webpack_require__(668); -var authToken = __webpack_require__(334); - -function _objectWithoutPropertiesLoose(source, excluded) { - if (source == null) return {}; - var target = {}; - var sourceKeys = Object.keys(source); - var key, i; - - for (i = 0; i < sourceKeys.length; i++) { - key = sourceKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - target[key] = source[key]; - } - - return target; -} - -function _objectWithoutProperties(source, excluded) { - if (source == null) return {}; - - var target = _objectWithoutPropertiesLoose(source, excluded); - - var key, i; - - if (Object.getOwnPropertySymbols) { - var sourceSymbolKeys = Object.getOwnPropertySymbols(source); - - for (i = 0; i < sourceSymbolKeys.length; i++) { - key = sourceSymbolKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; - target[key] = source[key]; - } - } - - return target; -} - -const VERSION = "3.6.0"; - -const _excluded = ["authStrategy"]; -class Octokit { - constructor(options = {}) { - const hook = new beforeAfterHook.Collection(); - const requestDefaults = { - baseUrl: request.request.endpoint.DEFAULTS.baseUrl, - headers: {}, - request: Object.assign({}, options.request, { - // @ts-ignore internal usage only, no need to type - hook: hook.bind(null, "request") - }), - mediaType: { - previews: [], - format: "" - } - }; // prepend default user agent with `options.userAgent` if set - - requestDefaults.headers["user-agent"] = [options.userAgent, `octokit-core.js/${VERSION} ${universalUserAgent.getUserAgent()}`].filter(Boolean).join(" "); - - if (options.baseUrl) { - requestDefaults.baseUrl = options.baseUrl; - } - - if (options.previews) { - requestDefaults.mediaType.previews = options.previews; - } - - if (options.timeZone) { - requestDefaults.headers["time-zone"] = options.timeZone; - } - - this.request = request.request.defaults(requestDefaults); - this.graphql = graphql.withCustomRequest(this.request).defaults(requestDefaults); - this.log = Object.assign({ - debug: () => {}, - info: () => {}, - warn: console.warn.bind(console), - error: console.error.bind(console) - }, options.log); - this.hook = hook; // (1) If neither `options.authStrategy` nor `options.auth` are set, the `octokit` instance - // is unauthenticated. The `this.auth()` method is a no-op and no request hook is registered. - // (2) If only `options.auth` is set, use the default token authentication strategy. - // (3) If `options.authStrategy` is set then use it and pass in `options.auth`. Always pass own request as many strategies accept a custom request instance. - // TODO: type `options.auth` based on `options.authStrategy`. - - if (!options.authStrategy) { - if (!options.auth) { - // (1) - this.auth = async () => ({ - type: "unauthenticated" - }); - } else { - // (2) - const auth = authToken.createTokenAuth(options.auth); // @ts-ignore ¯\_(ツ)_/¯ - - hook.wrap("request", auth.hook); - this.auth = auth; - } - } else { - const { - authStrategy - } = options, - otherOptions = _objectWithoutProperties(options, _excluded); - - const auth = authStrategy(Object.assign({ - request: this.request, - log: this.log, - // we pass the current octokit instance as well as its constructor options - // to allow for authentication strategies that return a new octokit instance - // that shares the same internal state as the current one. The original - // requirement for this was the "event-octokit" authentication strategy - // of https://github.com/probot/octokit-auth-probot. - octokit: this, - octokitOptions: otherOptions - }, options.auth)); // @ts-ignore ¯\_(ツ)_/¯ - - hook.wrap("request", auth.hook); - this.auth = auth; - } // apply plugins - // https://stackoverflow.com/a/16345172 - - - const classConstructor = this.constructor; - classConstructor.plugins.forEach(plugin => { - Object.assign(this, plugin(this, options)); - }); - } - - static defaults(defaults) { - const OctokitWithDefaults = class extends this { - constructor(...args) { - const options = args[0] || {}; - - if (typeof defaults === "function") { - super(defaults(options)); - return; - } - - super(Object.assign({}, defaults, options, options.userAgent && defaults.userAgent ? { - userAgent: `${options.userAgent} ${defaults.userAgent}` - } : null)); - } - - }; - return OctokitWithDefaults; - } - /** - * Attach a plugin (or many) to your Octokit instance. - * - * @example - * const API = Octokit.plugin(plugin1, plugin2, plugin3, ...) - */ - - - static plugin(...newPlugins) { - var _a; - - const currentPlugins = this.plugins; - const NewOctokit = (_a = class extends this {}, _a.plugins = currentPlugins.concat(newPlugins.filter(plugin => !currentPlugins.includes(plugin))), _a); - return NewOctokit; - } - -} -Octokit.VERSION = VERSION; -Octokit.plugins = []; - -exports.Octokit = Octokit; -//# sourceMappingURL=index.js.map - - /***/ }), /***/ 807: From 1d81a48a8816dc2f00854342a1b3332f8739e517 Mon Sep 17 00:00:00 2001 From: "T.J. Corrigan" Date: Tue, 1 Nov 2022 21:59:12 +0000 Subject: [PATCH 2/2] manually specify plugin --- dist/index.js | 214 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 212 insertions(+), 2 deletions(-) diff --git a/dist/index.js b/dist/index.js index 949f81ca5..a25fc1a01 100644 --- a/dist/index.js +++ b/dist/index.js @@ -13351,8 +13351,11 @@ var glob = __webpack_require__(90); // EXTERNAL MODULE: ./node_modules/@actions/io/lib/io.js var io = __webpack_require__(436); +// EXTERNAL MODULE: ./node_modules/@octokit/plugin-paginate-graphql/dist-node/index.js +var dist_node = __webpack_require__(883); + // EXTERNAL MODULE: ./node_modules/@octokit/plugin-retry/dist-node/index.js -var dist_node = __webpack_require__(745); +var plugin_retry_dist_node = __webpack_require__(745); // CONCATENATED MODULE: ./src/async-function.ts const AsyncFunction = Object.getPrototypeOf(async () => null).constructor; @@ -13429,6 +13432,7 @@ const wrapRequire = new Proxy(require, { + process.on('unhandledRejection', handleError); main().catch(handleError); async function main() { @@ -13450,7 +13454,7 @@ async function main() { opts.retry = retryOpts; if (requestOpts) opts.request = requestOpts; - const github = Object(lib_github.getOctokit)(token, opts, dist_node.retry); + const github = Object(lib_github.getOctokit)(token, opts, plugin_retry_dist_node.retry, dist_node.paginateGraphql); const script = Object(core.getInput)('script', { required: true }); // Using property/value shorthand on `require` (e.g. `{require}`) causes compilation errors. const result = await callAsyncFunction({ @@ -13690,6 +13694,212 @@ conversions["RegExp"] = function (V, opts) { module.exports = eval("require")("encoding"); +/***/ }), + +/***/ 883: +/***/ (function(__unusedmodule, exports) { + +"use strict"; + + +Object.defineProperty(exports, '__esModule', { value: true }); + +// Todo: Add link to explanation +const generateMessage = (path, cursorValue) => `The cursor at "${path.join(",")}" did not change its value "${cursorValue}" after a page transition. Please make sure your that your query is set up correctly.`; + +class MissingCursorChange extends Error { + constructor(pageInfo, cursorValue) { + super(generateMessage(pageInfo.pathInQuery, cursorValue)); + this.pageInfo = pageInfo; + this.cursorValue = cursorValue; + this.name = "MissingCursorChangeError"; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + } + +} + +class MissingPageInfo extends Error { + constructor(response) { + super(`No pageInfo property found in response. Please make sure to specify the pageInfo in your query. Response-Data: ${JSON.stringify(response, null, 2)}`); + this.response = response; + this.name = "MissingPageInfo"; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + } + +} + +const isObject = value => Object.prototype.toString.call(value) === "[object Object]"; + +function findPaginatedResourcePath(responseData) { + const paginatedResourcePath = deepFindPathToProperty(responseData, "pageInfo"); + + if (paginatedResourcePath.length === 0) { + throw new MissingPageInfo(responseData); + } + + return paginatedResourcePath; +} + +const deepFindPathToProperty = (object, searchProp, path = []) => { + for (const key of Object.keys(object)) { + const currentPath = [...path, key]; + const currentValue = object[key]; + + if (currentValue.hasOwnProperty(searchProp)) { + return currentPath; + } + + if (isObject(currentValue)) { + const result = deepFindPathToProperty(currentValue, searchProp, currentPath); + + if (result.length > 0) { + return result; + } + } + } + + return []; +}; +/** + * The interfaces of the "get" and "set" functions are equal to those of lodash: + * https://lodash.com/docs/4.17.15#get + * https://lodash.com/docs/4.17.15#set + * + * They are cut down to our purposes, but could be replaced by the lodash calls + * if we ever want to have that dependency. + */ + + +const get = (object, path) => { + return path.reduce((current, nextProperty) => current[nextProperty], object); +}; + +const set = (object, path, mutator) => { + const lastProperty = path[path.length - 1]; + const parentPath = [...path].slice(0, -1); + const parent = get(object, parentPath); + + if (typeof mutator === "function") { + parent[lastProperty] = mutator(parent[lastProperty]); + } else { + parent[lastProperty] = mutator; + } +}; + +const extractPageInfos = responseData => { + const pageInfoPath = findPaginatedResourcePath(responseData); + return { + pathInQuery: pageInfoPath, + pageInfo: get(responseData, [...pageInfoPath, "pageInfo"]) + }; +}; + +const isForwardSearch = givenPageInfo => { + return givenPageInfo.hasOwnProperty("hasNextPage"); +}; + +const getCursorFrom = pageInfo => isForwardSearch(pageInfo) ? pageInfo.endCursor : pageInfo.startCursor; + +const hasAnotherPage = pageInfo => isForwardSearch(pageInfo) ? pageInfo.hasNextPage : pageInfo.hasPreviousPage; + +const createIterator = octokit => { + return (query, initialParameters = {}) => { + let nextPageExists = true; + let parameters = { ...initialParameters + }; + return { + [Symbol.asyncIterator]: () => ({ + async next() { + if (!nextPageExists) return { + done: true, + value: {} + }; + const response = await octokit.graphql(query, parameters); + const pageInfoContext = extractPageInfos(response); + const nextCursorValue = getCursorFrom(pageInfoContext.pageInfo); + nextPageExists = hasAnotherPage(pageInfoContext.pageInfo); + + if (nextPageExists && nextCursorValue === parameters.cursor) { + throw new MissingCursorChange(pageInfoContext, nextCursorValue); + } + + parameters = { ...parameters, + cursor: nextCursorValue + }; + return { + done: false, + value: response + }; + } + + }) + }; + }; +}; + +const mergeResponses = (response1, response2) => { + if (Object.keys(response1).length === 0) { + return Object.assign(response1, response2); + } + + const path = findPaginatedResourcePath(response1); + const nodesPath = [...path, "nodes"]; + const newNodes = get(response2, nodesPath); + + if (newNodes) { + set(response1, nodesPath, values => { + return [...values, ...newNodes]; + }); + } + + const edgesPath = [...path, "edges"]; + const newEdges = get(response2, edgesPath); + + if (newEdges) { + set(response1, edgesPath, values => { + return [...values, ...newEdges]; + }); + } + + const pageInfoPath = [...path, "pageInfo"]; + set(response1, pageInfoPath, get(response2, pageInfoPath)); + return response1; +}; + +const createPaginate = octokit => { + const iterator = createIterator(octokit); + return async (query, initialParameters = {}) => { + let mergedResponse = {}; + + for await (const response of iterator(query, initialParameters)) { + mergedResponse = mergeResponses(mergedResponse, response); + } + + return mergedResponse; + }; +}; + +function paginateGraphql(octokit) { + octokit.graphql; + return { + graphql: Object.assign(octokit.graphql, { + paginate: Object.assign(createPaginate(octokit), { + iterator: createIterator(octokit) + }) + }) + }; +} + +exports.paginateGraphql = paginateGraphql; +//# sourceMappingURL=index.js.map + + /***/ }), /***/ 891: