@@ -23,7 +23,15 @@ export type Member = Awaited<
2323
2424type OwnerKeyType = "members" ;
2525
26- type RepoKeyType = "releases" | "descriptions" | "issue" | "issues" | "pr" | "prs" ;
26+ type RepoKeyType =
27+ | "releases"
28+ | "descriptions"
29+ | "issue"
30+ | "issues"
31+ | "pr"
32+ | "prs"
33+ | "discussion"
34+ | "discussions" ;
2735
2836export type ItemDetails = {
2937 comments : Awaited < ReturnType < Issues [ "listComments" ] > > [ "data" ] ;
@@ -44,6 +52,70 @@ export type PullRequestDetails = ItemDetails & {
4452 linkedIssues : LinkedItem [ ] ;
4553} ;
4654
55+ export type DiscussionDetails = {
56+ info : Discussion ;
57+ comments : DiscussionComment [ ] ;
58+ } ;
59+
60+ type AuthorAssociation =
61+ | "OWNER"
62+ | "MEMBER"
63+ | "COLLABORATOR"
64+ | "CONTRIBUTOR"
65+ | "FIRST_TIMER"
66+ | "FIRST_TIME_CONTRIBUTOR"
67+ | "MANNEQUIN"
68+ | "NONE" ;
69+ type TeamDiscussion = Awaited <
70+ ReturnType < InstanceType < typeof Octokit > [ "rest" ] [ "teams" ] [ "listDiscussionsInOrg" ] >
71+ > [ "data" ] [ number ] ;
72+ export type Discussion = {
73+ repository_url : string ;
74+ category : {
75+ id : number ;
76+ node_id : string ;
77+ repository_id : number ;
78+ emoji : `:${string } :`;
79+ name : string ;
80+ description : string ;
81+ created_at : string ;
82+ updated_at : string ;
83+ slug : string ;
84+ is_answerable : boolean ;
85+ } ;
86+ answer_html_url : string | null ;
87+ answer_chosen_at : string | null ;
88+ answer_chosen_by : TeamDiscussion [ "author" ] | null ;
89+ id : number ;
90+ user : TeamDiscussion [ "author" ] ;
91+ labels : never [ ] ;
92+ state : "open" | "closed" ;
93+ state_reason : "resolved" | null ;
94+ locked : boolean ;
95+ comments : TeamDiscussion [ "comments_count" ] ;
96+ author_association : AuthorAssociation ;
97+ active_lock_reason : null ;
98+ timeline_url : string ;
99+ } & Pick <
100+ TeamDiscussion ,
101+ "html_url" | "node_id" | "number" | "title" | "created_at" | "updated_at" | "body" | "reactions"
102+ > ;
103+ type TeamDiscussionComment = Awaited <
104+ ReturnType < InstanceType < typeof Octokit > [ "rest" ] [ "teams" ] [ "listDiscussionCommentsInOrg" ] >
105+ > [ "data" ] [ number ] ;
106+ export type DiscussionComment = {
107+ id : number ;
108+ parent_id : number | null ;
109+ child_comment_count : number ;
110+ repository_url : `${string } /${string } `;
111+ discussion_id : number ;
112+ author_association : AuthorAssociation ;
113+ user : TeamDiscussion [ "author" ] ;
114+ } & Pick <
115+ TeamDiscussionComment ,
116+ "node_id" | "html_url" | "created_at" | "updated_at" | "body" | "reactions"
117+ > ;
118+
47119export type LinkedItem = {
48120 number : number ;
49121 title : string ;
@@ -169,6 +241,7 @@ export class GitHubCache {
169241 * 2. calls the promise to get new data if no value is found in cache
170242 * 3. store this new value back in the cache with an optional TTL before returning the value.
171243 *
244+ * @returns a currying promise than handles everything needed for requests
172245 * @private
173246 */
174247 #processCached< RType extends Parameters < InstanceType < typeof Redis > [ "json" ] [ "set" ] > [ 2 ] > ( ) {
@@ -229,14 +302,16 @@ export class GitHubCache {
229302 owner : string ,
230303 repo : string ,
231304 id : number ,
232- type : ExtractStrict < RepoKeyType , "issue" | "pr" > | undefined = undefined
305+ type : ExtractStrict < RepoKeyType , "issue" | "pr" | "discussions" > | undefined = undefined
233306 ) {
234307 // Known type we assume the existence of
235308 switch ( type ) {
236309 case "issue" :
237310 return await this . getIssueDetails ( owner , repo , id ) ;
238311 case "pr" :
239312 return await this . getPullRequestDetails ( owner , repo , id ) ;
313+ case "discussions" :
314+ return await this . getDiscussionDetails ( owner , repo , id ) ;
240315 }
241316
242317 // Unknown type, try to find or null otherwise
@@ -247,12 +322,18 @@ export class GitHubCache {
247322 }
248323
249324 try {
250- // comes last because issues will also resolve for prs
325+ // doesn't come first because issues will also resolve for prs
251326 return await this . getIssueDetails ( owner , repo , id ) ;
252327 } catch ( err : unknown ) {
253328 console . error ( `Error trying to get issue details for ${ owner } /${ repo } : ${ err } ` ) ;
254329 }
255330
331+ try {
332+ return await this . getDiscussionDetails ( owner , repo , id ) ;
333+ } catch ( err : unknown ) {
334+ console . error ( `Error trying to get discussion details for ${ owner } /${ repo } : ${ err } ` ) ;
335+ }
336+
256337 return null ;
257338 }
258339
@@ -314,6 +395,43 @@ export class GitHubCache {
314395 ) ;
315396 }
316397
398+ /**
399+ * Get the discussion from the specified info.
400+ *
401+ * @param owner the GitHub repository owner
402+ * @param repo the GitHub repository name
403+ * @param id the discussion number
404+ * @returns the matching discussion
405+ * @throws Error if the discussion is not found
406+ */
407+ async getDiscussionDetails ( owner : string , repo : string , id : number ) {
408+ return await this . #processCached< DiscussionDetails > ( ) (
409+ this . #getRepoKey( owner , repo , "discussion" , id ) ,
410+ ( ) =>
411+ Promise . all ( [
412+ this . #octokit. request ( "GET /repos/{owner}/{repo}/discussions/{number}" , {
413+ owner,
414+ repo,
415+ number : id
416+ } ) ,
417+ this . #octokit. paginate < DiscussionComment > (
418+ "GET /repos/{owner}/{repo}/discussions/{number}/comments" ,
419+ {
420+ owner,
421+ repo,
422+ number : id ,
423+ per_page
424+ }
425+ )
426+ ] ) ,
427+ ( [ { data : discussion } , comments ] ) => ( {
428+ info : discussion ,
429+ comments
430+ } ) ,
431+ FULL_DETAILS_TTL
432+ ) ;
433+ }
434+
317435 /**
318436 * Get the pull requests linked to the given issue number.
319437 *
@@ -720,6 +838,32 @@ export class GitHubCache {
720838 ) ;
721839 }
722840
841+ /**
842+ * Get all the discussions for a given GitHub repository.
843+ *
844+ * @param owner the GitHub repository owner
845+ * @param repo the GitHub repository name
846+ * @returns a list of discussions, empty if not existing
847+ */
848+ async getAllDiscussions ( owner : string , repo : string ) {
849+ return await this . #processCached< Discussion [ ] > ( ) (
850+ this . #getRepoKey( owner , repo , "discussions" ) ,
851+ async ( ) => {
852+ try {
853+ return await this . #octokit. paginate < Discussion > ( "GET /repos/{owner}/{repo}/discussions" , {
854+ owner,
855+ repo,
856+ per_page
857+ } ) ;
858+ } catch {
859+ return [ ] as Discussion [ ] ;
860+ }
861+ } ,
862+ discussions => discussions ,
863+ FULL_DETAILS_TTL
864+ ) ;
865+ }
866+
723867 /**
724868 * Get the deprecation state of a package from its name.
725869 *
0 commit comments