Skip to content

Commit

Permalink
build: improve detection of endpoints which support pagination
Browse files Browse the repository at this point in the history
This improves the logic we use to figure out which API endpoints
support pagination. Today, we look for an ID which begins with
`list`, but it turns out that many endpoints with pagination
support don't follow this convention.

This instead switches to a slightly more complex hybrid approach.
We rely on looking for the `per_page` query parameter, which is
a good sign of pagination support. On top of that, we hardcode:

* (a) some operations which support the `per_page` parameter in
  the code, but where the OpenAPI specification is wrong
* (b) some operations with support the `per_page` paraneter but
  do pagination differently
* (c) some (actually one!) operations which still paginate in
  the way that this plugin supports (`Link` header discovery),
  but use different params

The cases for (a) have been flagged to the teams at GitHub.
To better solve (b) and (c), we will look at adding some
explicit pagination information to the schema.
  • Loading branch information
timrogers committed Jun 30, 2022
1 parent 490ae71 commit 5712b23
Showing 1 changed file with 40 additions and 3 deletions.
43 changes: 40 additions & 3 deletions scripts/update-endpoints/typescript.js
Expand Up @@ -11,10 +11,47 @@ const prettier = require("prettier");
const ENDPOINTS = require("./generated/endpoints.json");
const endpoints = [];

// All of these cases have been reported to the relevant team in GitHub.
const ENDPOINTS_WITH_UNDOCUMENTED_PER_PAGE_ATTRIBUTE = [
{ scope: 'users', id: 'list-blocked-by-authenticated-user' },
{ scope: 'orgs', id: 'list-blocked-users' },
{ scope: 'packages', id: 'list-packages-for-authenticated-user' },
{ scope: 'packages', id: 'list-packages-for-user' },
{ scope: 'packages', id: 'list-packages-for-organization' }
];

// Endpoints with a documented `per_page` query parameter that are, in fact,
// not paginated (or are paginated in an unusual way)
const ENDPOINTS_WITH_PER_PAGE_ATTRIBUTE_THAT_BEHAVE_DIFFERENTLY = [
// Only the `files` key inside the commit is paginated. The rest is duplicated across
// all pages. Handling this case properly requires custom code.
{ scope: 'repos', id: 'get-commit' },
// The [docs](https://docs.github.com/en/rest/commits/commits#compare-two-commits) make
// these ones sound like a special case too - they must be because they support pagination
// but doesn't return an array.
{ scope: 'repos', id: 'compare-commits' },
{ scope: 'repos', id: 'compare-commits-with-basehead' }
]

const hasMatchingEndpoint = (list, id, scope) => list.some(endpoint => endpoint.id === id && endpoint.scope === scope);

const hasPerPageParameter = (endpoint) => {
const parameterNames = endpoint.parameters.map(parameter => parameter.name);
return parameterNames.includes("per_page");
}

const isPaginatedEndpoint = (endpoint) => {
const { id, scope } = endpoint;

return (hasPerPageParameter(endpoint) && !hasMatchingEndpoint(ENDPOINTS_WITH_PER_PAGE_ATTRIBUTE_THAT_BEHAVE_DIFFERENTLY, id, scope)) ||
hasMatchingEndpoint(ENDPOINTS_WITH_UNDOCUMENTED_PER_PAGE_ATTRIBUTE, id, scope) ||
// The "List public repos" API paginates with the `since` parameter, but otherwise
// behaves normally in its use of the `Link` header.
(endpoint.id === 'list-public' && endpoint.url === '/repositories')
}

for (const endpoint of ENDPOINTS) {
// All paginating endpoints have an operation ID starting with "list",
// with the exception of search endpoints
if (!/^list\b/.test(endpoint.id) && endpoint.scope !== "search") {
if (!isPaginatedEndpoint(endpoint)) {
continue;
}

Expand Down

0 comments on commit 5712b23

Please sign in to comment.