Last active
January 8, 2025 17:57
-
-
Save markgarrigan/bc9a300de932afc48556c8de83a99ab6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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