generated from nhcarrigan/template
fix: paginaton needs to be more generic
Not every API is the same.
This commit is contained in:
@@ -47,10 +47,17 @@ if (uploadPath === "") {
|
|||||||
|
|
||||||
const orgs = [ "nhcarrigan", "nhcarrigan-private", "nhcarrigan-games" ];
|
const orgs = [ "nhcarrigan", "nhcarrigan-private", "nhcarrigan-games" ];
|
||||||
|
|
||||||
|
let totalReposProcessed = 0;
|
||||||
|
let totalReposSucceeded = 0;
|
||||||
|
let totalReposFailed = 0;
|
||||||
|
|
||||||
for (const org of orgs) {
|
for (const org of orgs) {
|
||||||
|
console.log(`\n=== Fetching repositories for org: ${org} ===`);
|
||||||
const repos = await paginatedFetch<Array<Repository>>(`${giteaUrl}/api/v1/orgs/${org}/repos`, 100, { headers: { authorization: `Bearer ${giteaToken}` } });
|
const repos = await paginatedFetch<Array<Repository>>(`${giteaUrl}/api/v1/orgs/${org}/repos`, 100, { headers: { authorization: `Bearer ${giteaToken}` } });
|
||||||
|
console.log(`Found ${repos.length.toString()} repositories in ${org}`);
|
||||||
|
|
||||||
for (const repo of repos) {
|
for (const repo of repos) {
|
||||||
|
totalReposProcessed = totalReposProcessed + 1;
|
||||||
console.log(`Checking if file exists in ${org}/${repo.name}`);
|
console.log(`Checking if file exists in ${org}/${repo.name}`);
|
||||||
const fileResponse = await fetch(`${giteaUrl}/api/v1/repos/${org}/${repo.name}/contents/${uploadPath}`, {
|
const fileResponse = await fetch(`${giteaUrl}/api/v1/repos/${org}/${repo.name}/contents/${uploadPath}`, {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -77,10 +84,12 @@ for (const org of orgs) {
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
totalReposFailed = totalReposFailed + 1;
|
||||||
console.error(`Failed to update ${fileName} in ${org}/${repo.name}: ${response.statusText}`);
|
console.error(`Failed to update ${fileName} in ${org}/${repo.name}: ${response.statusText}`);
|
||||||
console.error(await response.text());
|
console.error(await response.text());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
totalReposSucceeded = totalReposSucceeded + 1;
|
||||||
console.log(`Updated ${fileName} in ${org}/${repo.name}`);
|
console.log(`Updated ${fileName} in ${org}/${repo.name}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -99,10 +108,17 @@ for (const org of orgs) {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
totalReposFailed = totalReposFailed + 1;
|
||||||
console.error(`Failed to upload ${fileName} to ${org}/${repo.name}: ${response.statusText}`);
|
console.error(`Failed to upload ${fileName} to ${org}/${repo.name}: ${response.statusText}`);
|
||||||
console.error(await response.text());
|
console.error(await response.text());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
totalReposSucceeded = totalReposSucceeded + 1;
|
||||||
console.log(`Uploaded ${fileName} to ${org}/${repo.name}`);
|
console.log(`Uploaded ${fileName} to ${org}/${repo.name}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`\n=== Summary ===`);
|
||||||
|
console.log(`Total repositories processed: ${totalReposProcessed.toString()}`);
|
||||||
|
console.log(`Successfully uploaded/updated: ${totalReposSucceeded.toString()}`);
|
||||||
|
console.log(`Failed: ${totalReposFailed.toString()}`);
|
||||||
|
|||||||
@@ -7,12 +7,13 @@
|
|||||||
/**
|
/**
|
||||||
* Fetches a paginated resource from a URL. Automatically handles pagination,
|
* Fetches a paginated resource from a URL. Automatically handles pagination,
|
||||||
* and returns the complete data as an array.
|
* and returns the complete data as an array.
|
||||||
* @type {Array<Record<string, unknown>>} T - The type of data returned from the API endpoint. This should be an array of objects.
|
* @type {Array<Record<string, unknown>>} - The type of data returned from the API endpoint. This should be an array of objects.
|
||||||
* @param url - The URL to fetch.
|
* @param url - The URL to fetch.
|
||||||
* @param limit - The number of items to fetch per page.
|
* @param limit - The number of items to fetch per page.
|
||||||
* @param options - The standard fetch options object.
|
* @param options - The standard fetch options object.
|
||||||
* @returns The complete data as type T.
|
* @returns The complete data as type T.
|
||||||
*/
|
*/
|
||||||
|
// eslint-disable-next-line max-lines-per-function, max-statements -- We're doing some complex logic here.
|
||||||
export const paginatedFetch = async <T extends Array<Record<string, unknown>>>(
|
export const paginatedFetch = async <T extends Array<Record<string, unknown>>>(
|
||||||
url: string,
|
url: string,
|
||||||
limit: number,
|
limit: number,
|
||||||
@@ -22,15 +23,67 @@ export const paginatedFetch = async <T extends Array<Record<string, unknown>>>(
|
|||||||
let offset = 0;
|
let offset = 0;
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- This is a workaround to avoid type errors.
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- This is a workaround to avoid type errors.
|
||||||
const data: T = [] as unknown as T;
|
const data: T = [] as unknown as T;
|
||||||
let request = await fetch(`${url}?limit=${limit.toString()}&page=${page.toString()}&offset=${offset.toString()}`, options);
|
|
||||||
|
// First page
|
||||||
|
const firstUrl = `${url}?limit=${limit.toString()}&page=${page.toString()}&offset=${offset.toString()}`;
|
||||||
|
console.log(`Fetching page ${page.toString()} (offset ${offset.toString()}, limit ${limit.toString()})...`);
|
||||||
|
let request = await fetch(firstUrl, options);
|
||||||
|
|
||||||
|
if (!request.ok) {
|
||||||
|
throw new Error(`Failed to fetch ${firstUrl}: ${request.status.toString()} ${request.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
let response: T = await request.json();
|
let response: T = await request.json();
|
||||||
|
|
||||||
|
// Check if response is actually an array
|
||||||
|
if (!Array.isArray(response)) {
|
||||||
|
console.error(
|
||||||
|
"API response is not an array:",
|
||||||
|
typeof response,
|
||||||
|
Object.keys(response),
|
||||||
|
);
|
||||||
|
const errorMessage
|
||||||
|
= `Expected array response but got ${typeof response}. `
|
||||||
|
+ `Response keys: ${Object.keys(response).join(", ")}`;
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Page ${page.toString()}: Received ${response.length.toString()} items`);
|
||||||
data.push(...response);
|
data.push(...response);
|
||||||
while (response.length >= limit) {
|
|
||||||
|
/**
|
||||||
|
* Continue paginating while we get items back.
|
||||||
|
* Keep fetching until we get an empty array (0 items), which means we've reached the end.
|
||||||
|
*/
|
||||||
|
while (response.length > 0) {
|
||||||
page = page + 1;
|
page = page + 1;
|
||||||
offset = offset + limit;
|
offset = offset + limit;
|
||||||
request = await fetch(`${url}?limit=${limit.toString()}&page=${page.toString()}&offset=${offset.toString()}`, options);
|
const pageUrl = `${url}?limit=${limit.toString()}&page=${page.toString()}&offset=${offset.toString()}`;
|
||||||
|
console.log(`Fetching page ${page.toString()} (offset ${offset.toString()}, limit ${limit.toString()})...`);
|
||||||
|
request = await fetch(pageUrl, options);
|
||||||
|
|
||||||
|
if (!request.ok) {
|
||||||
|
console.error(`Failed to fetch page ${page.toString()}: ${request.status.toString()} ${request.statusText}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
response = await request.json();
|
response = await request.json();
|
||||||
|
|
||||||
|
if (!Array.isArray(response)) {
|
||||||
|
console.error(`Page ${page.toString()} response is not an array:`, typeof response);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Page ${page.toString()}: Received ${response.length.toString()} items`);
|
||||||
|
|
||||||
|
// If we get an empty array, we've reached the end
|
||||||
|
if (response.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
data.push(...response);
|
data.push(...response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`Total items fetched: ${data.length.toString()}`);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user