@@ -10,7 +10,11 @@ import type {
1010import type { Notification } from '../../typesGitHub' ;
1111import { listNotificationsForAuthenticatedUser } from '../api/client' ;
1212import { determineFailureType } from '../api/errors' ;
13- import { PullRequestDetailsFragmentDoc } from '../api/graphql/generated/graphql' ;
13+ import {
14+ DiscussionDetailsFragmentDoc ,
15+ IssueDetailsFragmentDoc ,
16+ PullRequestDetailsFragmentDoc ,
17+ } from '../api/graphql/generated/graphql' ;
1418import { getHeaders } from '../api/request' ;
1519import { getGitHubGraphQLUrl , getNumberFromUrl } from '../api/utils' ;
1620import { rendererLogError , rendererLogWarn } from '../logger' ;
@@ -134,36 +138,131 @@ export async function enrichNotifications(
134138 if ( ! settings . detailedNotifications ) {
135139 return notifications ;
136140 }
141+ type NotificationKind = 'PullRequest' | 'Issue' | 'Discussion' ;
142+
143+ type QueryConfig = {
144+ aliasPrefix : string ;
145+ fragment : string ;
146+ extras : Array < {
147+ name : string ;
148+ type : string ;
149+ defaultValue : number | boolean ;
150+ } > ;
151+ selection : ( index : number ) => string ;
152+ } ;
137153
138- // Build combined query for pull requests
139- let mergedQuery = '' ;
140- let i = 0 ;
141- const args : Array < { org : string ; repo : string ; number : number } > = [ ] ;
142-
143- for ( const notification of notifications ) {
144- if ( notification . subject . type === 'PullRequest' ) {
145- const org = notification . repository . owner . login ;
146- const repo = notification . repository . name ;
147- const number = getNumberFromUrl ( notification . subject . url ) ;
148-
149- args . push ( {
150- org,
151- repo,
152- number,
153- } ) ;
154-
155- mergedQuery += `pr${ i } : repository(owner: $owner${ i } , name: $name${ i } ) {
156- pullRequest(number: $number${ i } ) {
154+ const queryConfigs : Record < NotificationKind , QueryConfig > = {
155+ PullRequest : {
156+ aliasPrefix : 'pr' ,
157+ fragment : PullRequestDetailsFragmentDoc . toString ( ) ,
158+ extras : [
159+ { name : 'firstLabels' , type : 'Int' , defaultValue : 100 } ,
160+ { name : 'lastComments' , type : 'Int' , defaultValue : 100 } ,
161+ { name : 'lastReviews' , type : 'Int' , defaultValue : 100 } ,
162+ { name : 'firstClosingIssues' , type : 'Int' , defaultValue : 100 } ,
163+ ] ,
164+ selection : (
165+ index : number ,
166+ ) => `pr${ index } : repository(owner: $owner${ index } , name: $name${ index } ) {
167+ pullRequest(number: $number${ index } ) {
157168 ...PullRequestDetails
158169 }
159- }\n` ;
170+ }` ,
171+ } ,
172+ Issue : {
173+ aliasPrefix : 'issue' ,
174+ fragment : IssueDetailsFragmentDoc . toString ( ) ,
175+ extras : [
176+ { name : 'lastComments' , type : 'Int' , defaultValue : 100 } ,
177+ { name : 'firstLabels' , type : 'Int' , defaultValue : 100 } ,
178+ ] ,
179+ selection : (
180+ index : number ,
181+ ) => `issue${ index } : repository(owner: $owner${ index } , name: $name${ index } ) {
182+ issue(number: $number${ index } ) {
183+ ...IssueDetails
184+ }
185+ }` ,
186+ } ,
187+ Discussion : {
188+ aliasPrefix : 'discussion' ,
189+ fragment : DiscussionDetailsFragmentDoc . toString ( ) ,
190+ extras : [
191+ { name : 'lastComments' , type : 'Int' , defaultValue : 100 } ,
192+ { name : 'lastReplies' , type : 'Int' , defaultValue : 100 } ,
193+ { name : 'firstLabels' , type : 'Int' , defaultValue : 100 } ,
194+ { name : 'includeIsAnswered' , type : 'Boolean!' , defaultValue : true } ,
195+ ] ,
196+ selection : (
197+ index : number ,
198+ ) => `discussion${ index } : repository(owner: $owner${ index } , name: $name${ index } ) {
199+ discussion(number: $number${ index } ) {
200+ ...DiscussionDetails
201+ }
202+ }` ,
203+ } ,
204+ } ;
205+
206+ const selections : string [ ] = [ ] ;
207+ const variableDefinitions : string [ ] = [ ] ;
208+ const variableValues : Record < string , string | number | boolean > = { } ;
209+ const extraVariableDefinitions = new Map < string , string > ( ) ;
210+ const extraVariableValues : Record < string , number | boolean > = { } ;
211+ const fragments = new Map < string , string > ( ) ;
212+
213+ const collectFragments = ( doc : string ) => {
214+ const fragmentRegex =
215+ / f r a g m e n t \s + [ A - Z a - z 0 - 9 _ ] + \s + o n [ \s \S ] * ?(? = (?: f r a g m e n t \s + [ A - Z a - z 0 - 9 _ ] + \s + o n ) | $ ) / g;
216+ const nameRegex = / f r a g m e n t \s + ( [ A - Z a - z 0 - 9 _ ] + ) \s + o n / ;
217+
218+ const matches = doc . match ( fragmentRegex ) ?? [ ] ;
219+ for ( const match of matches ) {
220+ const nameMatch = match . match ( nameRegex ) ;
221+ if ( ! nameMatch ) {
222+ continue ;
223+ }
224+ const name = nameMatch [ 1 ] ;
225+ if ( ! fragments . has ( name ) ) {
226+ fragments . set ( name , match . trim ( ) ) ;
227+ }
228+ }
229+ } ;
160230
161- i += 1 ;
231+ let index = 0 ;
232+
233+ for ( const notification of notifications ) {
234+ const kind = notification . subject . type as NotificationKind ;
235+ const config = queryConfigs [ kind ] ;
236+
237+ if ( ! config ) {
238+ continue ;
239+ }
240+
241+ const org = notification . repository . owner . login ;
242+ const repo = notification . repository . name ;
243+ const number = getNumberFromUrl ( notification . subject . url ) ;
244+
245+ selections . push ( config . selection ( index ) ) ;
246+ variableDefinitions . push (
247+ `$owner${ index } : String!, $name${ index } : String!, $number${ index } : Int!` ,
248+ ) ;
249+ variableValues [ `owner${ index } ` ] = org ;
250+ variableValues [ `name${ index } ` ] = repo ;
251+ variableValues [ `number${ index } ` ] = number ;
252+
253+ for ( const extra of config . extras ) {
254+ if ( ! extraVariableDefinitions . has ( extra . name ) ) {
255+ extraVariableDefinitions . set ( extra . name , extra . type ) ;
256+ extraVariableValues [ extra . name ] = extra . defaultValue ;
257+ }
162258 }
259+
260+ collectFragments ( config . fragment ) ;
261+
262+ index += 1 ;
163263 }
164264
165- // If no pull requests, return early
166- if ( args . length === 0 ) {
265+ if ( selections . length === 0 ) {
167266 const enrichedNotifications = await Promise . all (
168267 notifications . map ( async ( notification : Notification ) => {
169268 return enrichNotification ( notification , settings ) ;
@@ -172,33 +271,26 @@ export async function enrichNotifications(
172271 return enrichedNotifications ;
173272 }
174273
175- let queryArgs = '' ;
176- const queryArgsVariables : Record < string , string | number > = { } ;
274+ const combinedVariableDefinitions = [
275+ ...variableDefinitions ,
276+ ...Array . from ( extraVariableDefinitions . entries ( ) ) . map (
277+ ( [ name , type ] ) => `$${ name } : ${ type } ` ,
278+ ) ,
279+ ] . join ( ', ' ) ;
177280
178- for ( let idx = 0 ; idx < args . length ; idx ++ ) {
179- const arg = args [ idx ] ;
180- if ( idx > 0 ) {
181- queryArgs += ', ' ;
182- }
183- queryArgs += `$owner${ idx } : String!, $name${ idx } : String!, $number${ idx } : Int!` ;
184- queryArgsVariables [ `owner${ idx } ` ] = arg . org ;
185- queryArgsVariables [ `name${ idx } ` ] = arg . repo ;
186- queryArgsVariables [ `number${ idx } ` ] = arg . number ;
187- }
281+ const mergedQuery = `query FetchMergedNotifications(${ combinedVariableDefinitions } ) {
282+ ${ selections . join ( '\n' ) }
283+ }
188284
189- // Add variables from PullRequestDetailsFragment
190- queryArgs +=
191- ', $firstLabels: Int, $lastComments: Int, $lastReviews: Int, $firstClosingIssues: Int' ;
192- queryArgsVariables [ 'firstLabels' ] = 100 ;
193- queryArgsVariables [ 'lastComments' ] = 100 ;
194- queryArgsVariables [ 'lastReviews' ] = 100 ;
195- queryArgsVariables [ 'firstClosingIssues' ] = 100 ;
285+ ${ Array . from ( fragments . values ( ) ) . join ( '\n' ) } `;
196286
197- const fragmentQuery = PullRequestDetailsFragmentDoc . toString ( ) ;
198- mergedQuery = `query FetchMergedPullRequests(${ queryArgs } ) {\n${ mergedQuery } }\n\n${ fragmentQuery } ` ;
287+ const queryVariables = {
288+ ...variableValues ,
289+ ...extraVariableValues ,
290+ } ;
199291
200292 console . log ( 'MERGED QUERY ' , JSON . stringify ( mergedQuery , null , 2 ) ) ;
201- console . log ( 'MERGED ARGS ' , JSON . stringify ( queryArgsVariables , null , 2 ) ) ;
293+ console . log ( 'MERGED ARGS ' , JSON . stringify ( queryVariables , null , 2 ) ) ;
202294
203295 try {
204296 const url = getGitHubGraphQLUrl (
@@ -213,14 +305,14 @@ export async function enrichNotifications(
213305 url,
214306 data : {
215307 query : mergedQuery ,
216- variables : queryArgsVariables ,
308+ variables : queryVariables ,
217309 } ,
218310 headers : headers ,
219311 } ) . then ( ( response ) => {
220312 console . log ( 'MERGED RESPONSE ' , JSON . stringify ( response , null , 2 ) ) ;
221313 } ) ;
222314 } catch ( err ) {
223- console . error ( 'Failed to fetch merged pull request details' , err ) ;
315+ console . error ( 'Failed to fetch merged notification details' , err ) ;
224316 }
225317
226318 const enrichedNotifications = await Promise . all (
0 commit comments