The Sonatype REST API paginates data in its response with a continuation token placed inside the response body; if the continuation token is null, you know you're on the last page of results.
I would like to recursively fetch all records from the endpoint with fetch (unless there is a better strategy?), updating the url using a query parameter on each recursive iteration and calling the recursive fetch on it, and building a storage structure containing the complete list of records in the function. I have been unsuccessful.
To get the first page of records, this works:
const requestUrl = `https://{baseURL}/service/rest/v1/components?repository=myRepo&group=myGroup`;
// returns first 10 results
async function fetchData(url) {
let headers = new Headers();
headers.append("Accept", "application/json");
headers.append(
"Authorization",
"Basic <BASE64-STRING-HERE>"
);
const requestOptions = {
method: "GET",
headers: headers,
redirect: "follow",
};
// Fetch request and parse as JSON
const response = await fetch(url, requestOptions);
let data = await response.json();
return data;
}
// called at `/api/get-data`
export default async function getData(req, res) {
try {
let result = await fetchData(requestUrl);
res.status(200).json({ result });
} catch (err) {
res.status(500).json({ error: "failed to load data" });
}
}
This returns an object containing an array of 10 records and the continuation token, a string, e.g.
{"items":[{"id": 1, "name": "itemOne"}, {"id": 2, "name": "itemTwo"}, etc.],"continuationToken":"07929bf0c78cf6c92d4acd0bf5823d80"}}
On the last page of records, the continuationToken is null:
{"items":[{"id": 99, "name": "itemNinetyNine"}, {"id": 100, "name": "itemOneHundred"}, etc.],"continuationToken": null}}
To get the complete set of records using recursion, this is the strategy I have tried, but haven't been successful:
async function recursivelyFetchData(url) {
let headers = new Headers();
headers.append("Accept", "application/json");
headers.append(
"Authorization",
"Basic <BASE-64-STRING>"
);
const requestOptions = {
method: "GET",
headers: headers,
redirect: "follow",
};
try {
// Fetch request and parse as JSON
const response = await fetch(url, requestOptions);
let data = await response.json();
// create a storage array
let storage = [];
// Set a variable for the next page url
let nextPageUrl;
// if the initial response contains a continuation token, start to recurse
if (data.continuationToken) {
// remove the query param so we don't keep adding it to the url string on each iteration
let strippedUrl = url.replace(/&continuationToken=.*/g, "");
// create the url with the query param
nextPageUrl =
strippedUrl + `&continuationToken=` + data.continuationToken;
// store the next response
let nextPageResponse = await recursivelyFetchData(
nextPageUrl,
requestOptions
);
// parse
let nextPageData = await nextPageResponse.json();
// add to storage
storage = [...data, ...nextPageData.items];
}
// break recursion
return storage;
} catch (err) {
res.status(500).json({ error: "failed to load data" });
}
}
export default async function getData(req, res) {
try {
let result = await recursivelyFetchData(requestUrl);
res.status(200).json({ result });
} catch (err) {
res.status(500).json({ error: "failed to load data in getPackages" });
}
}
I've tried the approaches found here, here, here, here, and here, but this API is different from other paginated responses in that it relies on a continuation token in the response body, and I can't for the life of me figure out how to recursively add the query parameter to the url, continue iterating until the continuation token is null, then break recursion.
Any help is appreciated.