Skip to content

Commit d8ff03f

Browse files
authored
refactor(discussions): optimize graphql queries (#861)
1 parent 1c47c85 commit d8ff03f

File tree

6 files changed

+81
-87
lines changed

6 files changed

+81
-87
lines changed

src/__mocks__/mockedData.ts

+4
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,8 @@ export const mockedGraphQLResponse: GraphQLSearch<DiscussionSearchResultNode> =
291291
{
292292
viewerSubscription: 'SUBSCRIBED',
293293
title: '1.16.0',
294+
isAnswered: false,
295+
stateReason: null,
294296
url: 'https://github.com/manosim/notifications-test/discussions/612',
295297
comments: {
296298
nodes: [
@@ -369,6 +371,8 @@ export const mockedGraphQLResponse: GraphQLSearch<DiscussionSearchResultNode> =
369371
{
370372
viewerSubscription: 'IGNORED',
371373
title: '1.16.0',
374+
isAnswered: false,
375+
stateReason: null,
372376
url: 'https://github.com/manosim/notifications-test/discussions/612',
373377
comments: {
374378
nodes: [

src/hooks/useNotifications.test.ts

+5-7
Original file line numberDiff line numberDiff line change
@@ -256,14 +256,12 @@ describe('hooks/useNotifications.ts', () => {
256256
.reply(200, {
257257
data: {
258258
search: {
259-
edges: [
259+
nodes: [
260260
{
261-
node: {
262-
title: 'This is an answered discussion',
263-
viewerSubscription: 'SUBSCRIBED',
264-
stateReason: null,
265-
isAnswered: true,
266-
},
261+
title: 'This is an answered discussion',
262+
viewerSubscription: 'SUBSCRIBED',
263+
stateReason: null,
264+
isAnswered: true,
267265
},
268266
],
269267
},

src/typesGithub.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -248,16 +248,11 @@ export interface GraphQLSearch<T> {
248248
};
249249
}
250250

251-
export interface DiscussionStateSearchResultNode {
251+
export interface DiscussionSearchResultNode {
252252
viewerSubscription: ViewerSubscription;
253253
title: string;
254254
stateReason: DiscussionStateType;
255255
isAnswered: boolean;
256-
}
257-
258-
export interface DiscussionSearchResultNode {
259-
viewerSubscription: ViewerSubscription;
260-
title: string;
261256
url: string;
262257
comments: {
263258
nodes: DiscussionCommentNode[];

src/utils/helpers.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ describe('utils/helpers.ts', () => {
398398

399399
const requestPromise = new Promise((resolve) =>
400400
resolve({
401-
data: {},
401+
data: { data: { search: { nodes: [] } } },
402402
} as AxiosResponse),
403403
) as AxiosPromise;
404404

src/utils/helpers.ts

+67-38
Original file line numberDiff line numberDiff line change
@@ -126,39 +126,80 @@ async function getDiscussionUrl(
126126
): Promise<string> {
127127
let url = `${notification.repository.html_url}/discussions`;
128128

129+
const discussion = await fetchDiscussion(notification, token, true);
130+
131+
if (discussion) {
132+
url = discussion.url;
133+
134+
let comments = discussion.comments.nodes;
135+
136+
let latestCommentId: string | number;
137+
138+
if (comments?.length) {
139+
latestCommentId = getLatestDiscussionCommentId(comments);
140+
url += `#discussioncomment-${latestCommentId}`;
141+
}
142+
}
143+
144+
return url;
145+
}
146+
147+
export async function fetchDiscussion(
148+
notification: Notification,
149+
token: string,
150+
includeComments: boolean,
151+
): Promise<DiscussionSearchResultNode | null> {
129152
const response: GraphQLSearch<DiscussionSearchResultNode> =
130153
await apiRequestAuth(`https://api.github.com/graphql`, 'POST', token, {
131-
query: `{
132-
search(query:"${formatSearchQueryString(
133-
notification.repository.full_name,
134-
notification.subject.title,
135-
notification.updated_at,
136-
)}", type: DISCUSSION, first: 10) {
137-
nodes {
138-
... on Discussion {
139-
viewerSubscription
140-
title
141-
url
142-
comments(last: 100) {
143-
nodes {
144-
databaseId
145-
createdAt
146-
replies(last: 1) {
147-
nodes {
148-
databaseId
149-
createdAt
154+
query: `query fetchDiscussions(
155+
$includeComments: Boolean!,
156+
$queryStatement: String!,
157+
$type: SearchType!,
158+
$firstDiscussions: Int,
159+
$lastComments: Int,
160+
$firstReplies: Int
161+
) {
162+
search(query:$queryStatement, type: $type, first: $firstDiscussions) {
163+
nodes {
164+
... on Discussion {
165+
viewerSubscription
166+
title
167+
stateReason
168+
isAnswered
169+
url
170+
comments(last: $lastComments) @include(if: $includeComments){
171+
nodes {
172+
databaseId
173+
createdAt
174+
replies(last: $firstReplies) {
175+
nodes {
176+
databaseId
177+
createdAt
178+
}
150179
}
151180
}
152181
}
153182
}
154183
}
155184
}
156185
}
157-
}`,
186+
`,
187+
variables: {
188+
queryStatement: formatSearchQueryString(
189+
notification.repository.full_name,
190+
notification.subject.title,
191+
notification.updated_at,
192+
),
193+
type: 'DISCUSSION',
194+
firstDiscussions: 10,
195+
lastComments: 100,
196+
firstReplies: 1,
197+
includeComments: includeComments,
198+
},
158199
});
159200

160201
let discussions =
161-
response?.data?.data?.search?.nodes?.filter(
202+
response?.data?.data.search.nodes.filter(
162203
(discussion) => discussion.title === notification.subject.title,
163204
) || [];
164205

@@ -167,29 +208,17 @@ async function getDiscussionUrl(
167208
(discussion) => discussion.viewerSubscription === 'SUBSCRIBED',
168209
);
169210

170-
if (discussions[0]) {
171-
const discussion = discussions[0];
172-
url = discussion.url;
173-
174-
let comments = discussion.comments.nodes;
175-
176-
let latestCommentId: string | number;
177-
if (comments?.length) {
178-
latestCommentId = getLatestDiscussionCommentId(comments);
179-
url += `#discussioncomment-${latestCommentId}`;
180-
}
181-
}
182-
183-
return url;
211+
return discussions[0];
184212
}
185213

186-
export const getLatestDiscussionCommentId = (
214+
export function getLatestDiscussionCommentId(
187215
comments: DiscussionCommentNode[],
188-
) =>
189-
comments
216+
) {
217+
return comments
190218
.flatMap((comment) => comment.replies.nodes)
191219
.concat([comments.at(-1)])
192220
.reduce((a, b) => (a.createdAt > b.createdAt ? a : b))?.databaseId;
221+
}
193222

194223
export async function generateGitHubWebUrl(
195224
notification: Notification,

src/utils/state.ts

+3-35
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import { formatSearchQueryString } from './helpers';
1+
import { fetchDiscussion } from './helpers';
22
import {
33
CheckSuiteAttributes,
44
CheckSuiteStatus,
5-
DiscussionStateSearchResultNode,
65
DiscussionStateType,
7-
GraphQLSearch,
86
Issue,
97
IssueStateReasonType,
108
IssueStateType,
@@ -84,39 +82,9 @@ export async function getDiscussionState(
8482
notification: Notification,
8583
token: string,
8684
): Promise<DiscussionStateType> {
87-
const response: GraphQLSearch<DiscussionStateSearchResultNode> =
88-
await apiRequestAuth(`https://api.github.com/graphql`, 'POST', token, {
89-
query: `{
90-
search(query:"${formatSearchQueryString(
91-
notification.repository.full_name,
92-
notification.subject.title,
93-
notification.updated_at,
94-
)}", type: DISCUSSION, first: 10) {
95-
nodes {
96-
... on Discussion {
97-
viewerSubscription
98-
title
99-
stateReason
100-
isAnswered
101-
}
102-
}
103-
}
104-
}`,
105-
});
106-
107-
let discussions =
108-
response?.data?.data?.search?.nodes?.filter(
109-
(discussion) => discussion.title === notification.subject.title,
110-
) || [];
111-
112-
if (discussions.length > 1) {
113-
discussions = discussions.filter(
114-
(discussion) => discussion.viewerSubscription === 'SUBSCRIBED',
115-
);
116-
}
85+
const discussion = await fetchDiscussion(notification, token, false);
11786

118-
if (discussions[0]) {
119-
const discussion = discussions[0];
87+
if (discussion) {
12088
if (discussion.isAnswered) {
12189
return 'ANSWERED';
12290
}

0 commit comments

Comments
 (0)