Skip to content

Instantly share code, notes, and snippets.

@markgarrigan
Last active January 8, 2025 17:57
Show Gist options
  • Save markgarrigan/bc9a300de932afc48556c8de83a99ab6 to your computer and use it in GitHub Desktop.
Save markgarrigan/bc9a300de932afc48556c8de83a99ab6 to your computer and use it in GitHub Desktop.
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, forkJoin, of } from 'rxjs';
import { switchMap, map, catchError } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class GithubService {
private apiUrl = 'https://api.github.com';
private token = 'your-personal-access-token'; // Optional
constructor(private http: HttpClient) {}
// Get user's organizations
getUserOrganizations(username: string): Observable<any[]> {
const headers = this.getAuthHeaders();
return this.http.get<any[]>(`${this.apiUrl}/users/${username}/orgs`, { headers });
}
// Get repositories for an organization with pagination
getOrganizationRepos(org: string): Observable<any[]> {
const headers = this.getAuthHeaders();
const perPage = 100; // Max results per page
// Fetch the first page to determine total count
return this.http.get<any[]>(`${this.apiUrl}/orgs/${org}/repos?per_page=${perPage}&page=1`, { observe: 'response', headers }).pipe(
switchMap((response) => {
const repos = response.body || [];
const linkHeader = response.headers.get('link');
const totalPages = this.getTotalPagesFromLinkHeader(linkHeader);
if (totalPages <= 1) {
return of(repos); // If there's only one page, return the repos directly
}
// Generate requests for remaining pages
const pageRequests: Observable<any[]>[] = [];
for (let i = 2; i <= totalPages; i++) {
pageRequests.push(
this.http.get<any[]>(`${this.apiUrl}/orgs/${org}/repos?per_page=${perPage}&page=${i}`, { headers })
);
}
return forkJoin(pageRequests).pipe(
map((pages) => repos.concat(...pages.flat())) // Combine all pages into a single array
);
})
);
}
// Get all template repositories from all organizations
getTemplateRepos(username: string): Observable<any[]> {
return this.getUserOrganizations(username).pipe(
map((orgs) => orgs.map((org) => org.login)), // Extract organization logins
switchMap((orgLogins) =>
forkJoin(orgLogins.map((org) => this.getOrganizationRepos(org))).pipe(
map((allRepos) => allRepos.flat().filter((repo) => repo.is_template)) // Filter for template repos
)
),
catchError((error) => {
console.error('Error fetching template repositories:', error);
return of([]); // Return an empty array on error
})
);
}
private getAuthHeaders(): HttpHeaders {
return this.token ? new HttpHeaders({ Authorization: `Bearer ${this.token}` }) : new HttpHeaders();
}
// Parse the `link` header to get the total number of pages
private getTotalPagesFromLinkHeader(linkHeader: string | null): number {
if (!linkHeader) {
return 1;
}
const match = linkHeader.match(/&page=(\d+)>; rel="last"/);
return match ? parseInt(match[1], 10) : 1;
}
}
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, forkJoin } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class GithubService {
private apiUrl = 'https://api.github.com';
private token = 'your-personal-access-token'; // Optional
constructor(private http: HttpClient) {}
// Get user's organizations
getUserOrganizations(username: string): Observable<any[]> {
const headers = this.getAuthHeaders();
return this.http.get<any[]>(`${this.apiUrl}/users/${username}/orgs`, { headers });
}
// Get repositories for an organization
getOrganizationRepos(org: string): Observable<any[]> {
const headers = this.getAuthHeaders();
return this.http.get<any[]>(`${this.apiUrl}/orgs/${org}/repos`, { headers });
}
// Get all template repositories from all organizations
getTemplateRepos(username: string): Observable<any[]> {
return this.getUserOrganizations(username).pipe(
map((orgs) => orgs.map((org) => org.login)), // Extract organization logins
map((orgLogins) =>
orgLogins.map((org) => this.getOrganizationRepos(org)) // Create observables for each org's repos
),
map((orgRequests) => forkJoin(orgRequests)), // Combine all org requests
map((orgsRepos$: Observable<any[]>) =>
orgsRepos$.pipe(
map((orgRepos) =>
orgRepos.flat().filter((repo: any) => repo.is_template) // Filter template repos
)
)
),
(orgsRepos$) => orgsRepos$.flat() // Flatten the array of repos
);
}
private getAuthHeaders(): HttpHeaders {
return this.token ? new HttpHeaders({ Authorization: `Bearer ${this.token}` }) : new HttpHeaders();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment