@@ -17,19 +17,28 @@ export type GitHubRelease = Awaited<
1717 ReturnType < InstanceType < typeof Octokit > [ "rest" ] [ "repos" ] [ "listReleases" ] >
1818> [ "data" ] [ number ] ;
1919
20- type KeyType = "releases" | "descriptions" | "issue" | "pr" ;
20+ export type Member = Awaited <
21+ ReturnType < InstanceType < typeof Octokit > [ "rest" ] [ "orgs" ] [ "listMembers" ] >
22+ > [ "data" ] [ number ] ;
23+
24+ type OwnerKeyType = "members" ;
25+
26+ type RepoKeyType = "releases" | "descriptions" | "issue" | "issues" | "pr" | "prs" ;
2127
2228export type ItemDetails = {
2329 comments : Awaited < ReturnType < Issues [ "listComments" ] > > [ "data" ] ;
2430} ;
2531
32+ export type Issue = Awaited < ReturnType < Issues [ "get" ] > > [ "data" ] ;
2633export type IssueDetails = ItemDetails & {
27- info : Awaited < ReturnType < Issues [ "get" ] > > [ "data" ] ;
34+ info : Issue ;
2835 linkedPrs : LinkedItem [ ] ;
2936} ;
3037
38+ export type PullRequest = Awaited < ReturnType < Pulls [ "get" ] > > [ "data" ] ;
39+ export type ListedPullRequest = Awaited < ReturnType < Pulls [ "list" ] > > [ "data" ] [ number ] ;
3140export type PullRequestDetails = ItemDetails & {
32- info : Awaited < ReturnType < Pulls [ "get" ] > > [ "data" ] ;
41+ info : PullRequest ;
3342 commits : Awaited < ReturnType < Pulls [ "listCommits" ] > > [ "data" ] ;
3443 files : Awaited < ReturnType < Pulls [ "listFiles" ] > > [ "data" ] ;
3544 linkedIssues : LinkedItem [ ] ;
@@ -70,6 +79,10 @@ const FULL_DETAILS_TTL = 60 * 60 * 2; // 2 hours
7079 * The TTL of the cached descriptions, in seconds.
7180 */
7281const DESCRIPTIONS_TTL = 60 * 60 * 24 * 10 ; // 10 days
82+ /**
83+ * The TTL of organization members, in seconds.
84+ */
85+ const MEMBERS_TTL = 60 * 60 * 24 * 2 ; // 2 days
7386/**
7487 * The TTL for non-deprecated packages, in seconds
7588 */
@@ -102,6 +115,22 @@ export class GitHubCache {
102115 } ) ;
103116 }
104117
118+ /**
119+ * Generates a Redis key from the passed info.
120+ *
121+ * @param owner the GitHub repository owner
122+ * @param type the kind of cache to use
123+ * @param args the optional additional values to append
124+ * at the end of the key; every element will be interpolated
125+ * in a string
126+ * @returns the pure computed key
127+ * @private
128+ */
129+ #getOwnerKey( owner : string , type : OwnerKeyType , ...args : unknown [ ] ) {
130+ const strArgs = args . map ( a => `:${ a } ` ) . join ( "" ) ;
131+ return `owner:${ owner } :${ type } ${ strArgs } ` ;
132+ }
133+
105134 /**
106135 * Generates a Redis key from the passed info.
107136 *
@@ -114,8 +143,8 @@ export class GitHubCache {
114143 * @returns the pure computed key
115144 * @private
116145 */
117- #getRepoKey( owner : string , repo : string , type : KeyType , ...args : unknown [ ] ) {
118- const strArgs = args . map ( a => `:${ a } ` ) ;
146+ #getRepoKey( owner : string , repo : string , type : RepoKeyType , ...args : unknown [ ] ) {
147+ const strArgs = args . map ( a => `:${ a } ` ) . join ( "" ) ;
119148 return `repo:${ owner } /${ repo } :${ type } ${ strArgs } ` ;
120149 }
121150
@@ -130,7 +159,7 @@ export class GitHubCache {
130159 * @private
131160 */
132161 #getPackageKey( packageName : string , ...args : unknown [ ] ) {
133- const strArgs = args . map ( a => `:${ a } ` ) ;
162+ const strArgs = args . map ( a => `:${ a } ` ) . join ( "" ) ;
134163 return `package:${ packageName } ${ strArgs } ` ;
135164 }
136165
@@ -200,7 +229,7 @@ export class GitHubCache {
200229 owner : string ,
201230 repo : string ,
202231 id : number ,
203- type : ExtractStrict < KeyType , "issue" | "pr" > | undefined = undefined
232+ type : ExtractStrict < RepoKeyType , "issue" | "pr" > | undefined = undefined
204233 ) {
205234 // Known type we assume the existence of
206235 switch ( type ) {
@@ -612,6 +641,85 @@ export class GitHubCache {
612641 ) ;
613642 }
614643
644+ /**
645+ * Get the list of members for a given organization.
646+ *
647+ * @param owner the GitHub organization name
648+ * @returns a list of members, or `undefined` if not existing
649+ */
650+ async getOrganizationMembers ( owner : string ) {
651+ return await this . #processCached< Member [ ] > ( ) (
652+ this . #getOwnerKey( owner , "members" ) ,
653+ async ( ) => {
654+ try {
655+ const { data : members } = await this . #octokit. rest . orgs . listPublicMembers ( {
656+ org : owner ,
657+ per_page
658+ } ) ;
659+ return members ;
660+ } catch {
661+ return [ ] as Member [ ] ;
662+ }
663+ } ,
664+ members => members ,
665+ MEMBERS_TTL
666+ ) ;
667+ }
668+
669+ /**
670+ * Get all the issues for a given GitHub repository.
671+ *
672+ * @param owner the GitHub repository owner
673+ * @param repo the GitHub repository name
674+ * @returns a list of issues, empty if not existing
675+ */
676+ async getAllIssues ( owner : string , repo : string ) {
677+ return await this . #processCached< Issue [ ] > ( ) (
678+ this . #getRepoKey( owner , repo , "issues" ) ,
679+ async ( ) => {
680+ try {
681+ const { data : issues } = await this . #octokit. rest . issues . listForRepo ( {
682+ owner,
683+ repo,
684+ per_page
685+ } ) ;
686+ return issues ;
687+ } catch {
688+ return [ ] as Issue [ ] ;
689+ }
690+ } ,
691+ issues => issues ,
692+ FULL_DETAILS_TTL
693+ ) ;
694+ }
695+
696+ /**
697+ * Get all the pull requests for a given GitHub repository.
698+ *
699+ * @param owner the GitHub repository owner
700+ * @param repo the GitHub repository name
701+ * @returns a list of pull requests, empty if not existing
702+ */
703+ async getAllPRs ( owner : string , repo : string ) {
704+ return await this . #processCached< ListedPullRequest [ ] > ( ) (
705+ this . #getRepoKey( owner , repo , "prs" ) ,
706+ async ( ) => {
707+ try {
708+ const { data : prs } = await this . #octokit. rest . pulls . list ( {
709+ owner,
710+ repo,
711+ per_page
712+ } ) ;
713+ return prs ;
714+ } catch {
715+ return [ ] as ListedPullRequest [ ] ;
716+ }
717+ } ,
718+ prs => prs ,
719+ FULL_DETAILS_TTL
720+ ) ;
721+ }
722+
615723 /**
616724 * Get the deprecation state of a package from its name.
617725 *
0 commit comments