Utilize pagination when querying GraalPy GitHub releases

This commit is contained in:
Michael Simacek 2023-08-29 13:34:19 +02:00
parent 62fe528f26
commit feb18948f5
3 changed files with 64 additions and 10 deletions

View File

@ -11,7 +11,8 @@ import {
isCacheFeatureAvailable,
getVersionInputFromFile,
getVersionInputFromPlainFile,
getVersionInputFromTomlFile
getVersionInputFromTomlFile,
getNextPageUrl
} from '../src/utils';
jest.mock('@actions/cache');
@ -136,3 +137,25 @@ describe('Version from file test', () => {
}
);
});
describe('getNextPageUrl', () => {
it('GitHub API pagination next page is parsed correctly', () => {
function generateResponse(link: string) {
return {
statusCode: 200,
result: null,
headers: {
link: link
}
};
}
const page1Links =
'<https://api.github.com/repositories/129883600/releases?page=2>; rel="next", <https://api.github.com/repositories/129883600/releases?page=3>; rel="last"';
expect(getNextPageUrl(generateResponse(page1Links))).toStrictEqual(
'https://api.github.com/repositories/129883600/releases?page=2'
);
const page2Links =
'<https://api.github.com/repositories/129883600/releases?page=1>; rel="prev", <https://api.github.com/repositories/129883600/releases?page=1>; rel="first"';
expect(getNextPageUrl(generateResponse(page2Links))).toBeNull();
});
});

View File

@ -14,7 +14,8 @@ import {
IGraalPyManifestRelease,
createSymlinkInFolder,
isNightlyKeyword,
getBinaryDirectory
getBinaryDirectory,
getNextPageUrl
} from './utils';
const TOKEN = core.getInput('token');
@ -106,7 +107,6 @@ export async function installGraalPy(
}
export async function getAvailableGraalPyVersions() {
const url = 'https://api.github.com/repos/oracle/graalpython/releases';
const http: httpm.HttpClient = new httpm.HttpClient('tool-cache');
let headers: ifm.IHeaders = {};
@ -114,14 +114,22 @@ export async function getAvailableGraalPyVersions() {
headers.authorization = AUTH;
}
const response = await http.getJson<IGraalPyManifestRelease[]>(url, headers);
if (!response.result) {
throw new Error(
`Unable to retrieve the list of available GraalPy versions from '${url}'`
);
}
let url: string | null =
'https://api.github.com/repos/oracle/graalpython/releases';
const result: IGraalPyManifestRelease[] = [];
do {
const response: ifm.ITypedResponse<IGraalPyManifestRelease[]> =
await http.getJson(url, headers);
if (!response.result) {
throw new Error(
`Unable to retrieve the list of available GraalPy versions from '${url}'`
);
}
result.push(...response.result);
url = getNextPageUrl(response);
} while (url);
return response.result;
return result;
}
async function createGraalPySymlink(

View File

@ -6,6 +6,7 @@ import * as path from 'path';
import * as semver from 'semver';
import * as toml from '@iarna/toml';
import * as exec from '@actions/exec';
import * as ifm from '@actions/http-client/interfaces';
export const IS_WINDOWS = process.platform === 'win32';
export const IS_LINUX = process.platform === 'linux';
@ -271,3 +272,25 @@ export function getVersionInputFromFile(versionFile: string): string[] {
export function getBinaryDirectory(installDir: string) {
return IS_WINDOWS ? installDir : path.join(installDir, 'bin');
}
/**
* Extract next page URL from a HTTP response "link" header. Such headers are used in GitHub APIs.
*/
export function getNextPageUrl<T>(response: ifm.ITypedResponse<T>) {
const responseHeaders = <ifm.IHeaders>response.headers;
const linkHeader = responseHeaders.link;
if (typeof linkHeader === 'string') {
for (let link of linkHeader.split(/\s*,\s*/)) {
const match = link.match(/<([^>]+)>(.*)/);
if (match) {
const url = match[1];
for (let param of match[2].split(/\s*;\s*/)) {
if (param.match(/rel="?next"?/)) {
return url;
}
}
}
}
}
return null;
}