Skip to content

Commit 6d53584

Browse files
committed
feat(api): codegen for discussions
Signed-off-by: Adam Setch <[email protected]>
1 parent 6340374 commit 6d53584

File tree

13 files changed

+219
-412
lines changed

13 files changed

+219
-412
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"build:main": "webpack --config ./config/webpack.config.main.prod.ts",
1010
"build:preload": "webpack --config ./config/webpack.config.preload.prod.ts",
1111
"build:renderer": "webpack --config ./config/webpack.config.renderer.prod.ts",
12-
"watch": "concurrently --names \"main,preload,renderer\" --prefix-colors \"blue,magenta,green\" \"pnpm watch:main\" \"pnpm watch:preload\" \"pnpm watch:renderer\"",
12+
"watch": "concurrently --names \"main,preload,renderer,codegen\" --prefix-colors \"blue,magenta,green,cyan\" \"pnpm watch:main\" \"pnpm watch:preload\" \"pnpm watch:renderer\" \"pnpm watch:codegen\"",
13+
"watch:codegen": "pnpm codegen --watch",
1314
"watch:main": "webpack --watch --config ./config/webpack.config.main.base.ts",
1415
"watch:preload": "webpack --watch --config ./config/webpack.config.preload.base.ts",
1516
"watch:renderer": "webpack --watch --config ./config/webpack.config.renderer.base.ts",

src/renderer/utils/api/__mocks__/response-mocks.ts

Lines changed: 39 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@ import {
44
} from '../../../__mocks__/account-mocks';
55
import type { Link } from '../../../types';
66
import type { Notification, Repository, User } from '../../../typesGitHub';
7-
import { makeFragmentData } from '../graphql/generated/fragment-masking';
8-
import type { FetchDiscussionsQuery } from '../graphql/generated/graphql';
9-
import {
10-
CommentFieldsFragmentDoc,
11-
DiscussionCommentFieldsFragmentDoc,
12-
DiscussionFieldsFragmentDoc,
13-
} from '../graphql/generated/graphql';
7+
import type { FetchDiscussionByNumberQuery } from '../graphql/generated/graphql';
148

159
export const mockNotificationUser: User = {
1610
login: 'octocat',
@@ -403,76 +397,56 @@ const mockDiscussionReplier = {
403397
};
404398

405399
const mockDiscussionComments = {
400+
totalCount: 2,
406401
nodes: [
407-
makeFragmentData(
408-
{
409-
__typename: 'DiscussionComment' as const,
410-
databaseId: 2258799,
411-
createdAt: '2017-02-20T17:51:57Z',
412-
author: mockDiscussionAuthor,
413-
replies: {
414-
__typename: 'DiscussionCommentConnection' as const,
415-
nodes: [
416-
makeFragmentData(
417-
{
418-
__typename: 'DiscussionComment' as const,
419-
databaseId: 2300902,
420-
createdAt: '2017-05-20T17:51:57Z',
421-
author: mockDiscussionReplier,
422-
},
423-
CommentFieldsFragmentDoc,
424-
),
425-
],
426-
},
402+
{
403+
__typename: 'DiscussionComment' as const,
404+
databaseId: 2258799,
405+
createdAt: '2017-02-20T17:51:57Z',
406+
author: mockDiscussionAuthor,
407+
replies: {
408+
__typename: 'DiscussionCommentConnection' as const,
409+
nodes: [
410+
{
411+
__typename: 'DiscussionComment' as const,
412+
databaseId: 2300902,
413+
createdAt: '2017-05-20T17:51:57Z',
414+
author: mockDiscussionReplier,
415+
replies: {
416+
__typename: 'DiscussionCommentConnection' as const,
417+
nodes: [],
418+
},
419+
},
420+
],
427421
},
428-
DiscussionCommentFieldsFragmentDoc,
429-
),
422+
},
430423
],
431-
totalCount: 2,
432424
};
433425

434426
const mockDiscussionLabels = {
435-
__typename: 'LabelConnection' as const,
436427
nodes: [
437428
{
438-
__typename: 'Label' as const,
439429
name: 'enhancement',
440430
},
441431
],
442432
};
443433

444-
export const mockGraphQLResponse: FetchDiscussionsQuery = {
445-
search: {
446-
__typename: 'SearchResultItemConnection',
447-
nodes: [
448-
makeFragmentData(
449-
{
450-
__typename: 'Discussion' as const,
451-
number: 123,
452-
title: '1.16.0',
453-
isAnswered: false,
454-
stateReason: null,
455-
url: 'https://github.com/gitify-app/notifications-test/discussions/612' as Link,
456-
author: {
457-
__typename: 'User' as const,
458-
' $fragmentRefs': {
459-
AuthorFields_User_Fragment: {
460-
__typename: 'User' as const,
461-
login: 'discussion-creator',
462-
url: 'https://github.com/discussion-creator' as Link,
463-
avatar_url:
464-
'https://avatars.githubusercontent.com/u/123456789?v=4' as Link,
465-
type: 'User' as const,
466-
},
467-
},
468-
},
469-
comments: mockDiscussionComments,
470-
labels: mockDiscussionLabels,
471-
},
472-
DiscussionFieldsFragmentDoc,
473-
),
474-
],
475-
},
476-
};
434+
export const mockDiscussionByNumberGraphQLResponse: FetchDiscussionByNumberQuery =
435+
{
436+
repository: {
437+
__typename: 'Repository',
438+
discussion: {
439+
__typename: 'Discussion' as const,
440+
number: 123,
441+
title: '1.16.0',
442+
isAnswered: false,
443+
stateReason: null,
444+
url: 'https://github.com/gitify-app/notifications-test/discussions/612' as Link,
445+
author: mockDiscussionAuthor,
446+
comments: mockDiscussionComments,
447+
labels: mockDiscussionLabels,
448+
},
449+
},
450+
};
477451

478452
export const mockSingleNotification: Notification = mockGitHubNotifications[0];

src/renderer/utils/api/client.ts

Lines changed: 14 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ import type {
2121
} from '../../typesGitHub';
2222
import { isAnsweredDiscussionFeatureSupported } from '../features';
2323
import { rendererLogError } from '../logger';
24-
import { graphql } from './graphql/generated';
2524
import {
26-
type DiscussionFieldsFragment,
27-
FetchDiscussionsDocument,
28-
type FetchDiscussionsQuery,
29-
type MeQuery,
25+
FetchDiscussionByNumberDocument,
26+
type FetchDiscussionByNumberQuery,
3027
} from './graphql/generated/graphql';
31-
import { formatAsGitHubSearchSyntax } from './graphql/utils';
3228
import { apiRequestAuth, performGraphQLRequest } from './request';
3329
import type { GitHubGraphQLResponse } from './types';
34-
import { getGitHubAPIBaseUrl, getGitHubGraphQLUrl } from './utils';
30+
import {
31+
getGitHubAPIBaseUrl,
32+
getGitHubGraphQLUrl,
33+
getNumberFromUrl,
34+
} from './utils';
3535

3636
/**
3737
* Get the authenticated user
@@ -239,21 +239,20 @@ export async function getHtmlUrl(url: Link, token: Token): Promise<string> {
239239
* Returns the latest discussion and their latest comments / replies
240240
*
241241
*/
242-
export async function searchDiscussions(
242+
export async function fetchDiscussionByNumber(
243243
notification: Notification,
244-
): Promise<GitHubGraphQLResponse<FetchDiscussionsQuery>> {
244+
): Promise<GitHubGraphQLResponse<FetchDiscussionByNumberQuery>> {
245245
const url = getGitHubGraphQLUrl(notification.account.hostname);
246+
const number = getNumberFromUrl(notification.subject.url);
246247

247248
return performGraphQLRequest(
248249
url.toString() as Link,
249250
notification.account.token,
250-
FetchDiscussionsDocument,
251+
FetchDiscussionByNumberDocument,
251252
{
252-
queryStatement: formatAsGitHubSearchSyntax(
253-
notification.repository.full_name,
254-
notification.subject.title,
255-
),
256-
firstDiscussions: 1,
253+
owner: notification.repository.owner.login,
254+
name: notification.repository.name,
255+
number: number,
257256
lastComments: 100,
258257
lastReplies: 100,
259258
firstLabels: 100,
@@ -263,51 +262,3 @@ export async function searchDiscussions(
263262
},
264263
);
265264
}
266-
267-
/**
268-
* Search for Discussions that match notification title and repository.
269-
*
270-
* Returns the latest discussion and their latest comments / replies
271-
*
272-
*/
273-
export async function searchDiscussionsv2(
274-
notification: Notification,
275-
): Promise<GitHubGraphQLResponse<MeQuery>> {
276-
const url = getGitHubGraphQLUrl(notification.account.hostname);
277-
278-
const Me = graphql(`
279-
query me {
280-
viewer {
281-
name
282-
}
283-
}
284-
`);
285-
286-
return performGraphQLRequest(
287-
url.toString() as Link,
288-
notification.account.token,
289-
Me,
290-
);
291-
}
292-
293-
/**
294-
* Return the latest discussion that matches the notification title and repository.
295-
*/
296-
export async function getLatestDiscussion(notification: Notification) {
297-
try {
298-
const response = await searchDiscussions(notification);
299-
return (
300-
(response.data.search.nodes.find(
301-
(discussion: DiscussionFieldsFragment) =>
302-
discussion.title === notification.subject.title,
303-
) as DiscussionFieldsFragment) ?? null
304-
);
305-
} catch (err) {
306-
rendererLogError(
307-
'getLatestDiscussion',
308-
'failed to fetch latest discussion for notification',
309-
err,
310-
notification,
311-
);
312-
}
313-
}

src/renderer/utils/api/graphql/discussions.ts

Lines changed: 31 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,53 +19,42 @@ export const CommentFieldsFragment = graphql(`
1919
}
2020
`);
2121

22-
export const DiscussionCommentFragment = graphql(`
23-
fragment DiscussionCommentFields on DiscussionComment {
24-
...CommentFields
25-
replies(last: $lastReplies) {
26-
nodes {
27-
...CommentFields
28-
}
29-
}
30-
}`);
31-
32-
export const DiscussionFieldsFragment = graphql(`
33-
fragment DiscussionFields on Discussion {
34-
number
35-
title
36-
stateReason
37-
isAnswered @include(if: $includeIsAnswered)
38-
url
39-
author {
40-
...AuthorFields
41-
}
42-
comments(last: $lastComments) {
43-
totalCount
44-
nodes {
45-
...DiscussionCommentFields
46-
}
47-
}
48-
labels(first: $firstLabels) {
49-
nodes {
50-
name
51-
}
52-
}
53-
}
54-
`);
55-
56-
export const FetchDiscussions = graphql(`
57-
query fetchDiscussions(
58-
$queryStatement: String!
59-
$firstDiscussions: Int
22+
export const FetchDiscussion = graphql(`
23+
query fetchDiscussionByNumber(
24+
$owner: String!
25+
$name: String!
26+
$number: Int!
6027
$lastComments: Int
6128
$lastReplies: Int
6229
$firstLabels: Int
6330
$includeIsAnswered: Boolean!
6431
) {
65-
search(query: $queryStatement, type: DISCUSSION, first: $firstDiscussions) {
66-
nodes {
67-
... on Discussion {
68-
...DiscussionFields
32+
repository(owner: $owner, name: $name) {
33+
discussion(number: $number) {
34+
__typename
35+
number
36+
title
37+
stateReason
38+
isAnswered @include(if: $includeIsAnswered)
39+
url
40+
author {
41+
...AuthorFields
42+
}
43+
comments(last: $lastComments) {
44+
totalCount
45+
nodes {
46+
...CommentFields
47+
replies(last: $lastReplies) {
48+
nodes {
49+
...CommentFields
50+
}
51+
}
52+
}
53+
}
54+
labels(first: $firstLabels) {
55+
nodes {
56+
name
57+
}
6958
}
7059
}
7160
}

src/renderer/utils/api/graphql/generated/gql.ts

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,16 @@ import * as types from './graphql';
1515
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
1616
*/
1717
type Documents = {
18-
"\n query me {\n viewer {\n name\n }\n }\n ": typeof types.MeDocument,
1918
"\n fragment AuthorFields on Actor {\n login\n url\n avatar_url: avatarUrl\n type: __typename\n }\n": typeof types.AuthorFieldsFragmentDoc,
2019
"\n fragment CommentFields on DiscussionComment {\n databaseId\n createdAt\n author {\n ...AuthorFields\n }\n }\n": typeof types.CommentFieldsFragmentDoc,
21-
"\n fragment DiscussionCommentFields on DiscussionComment {\n ...CommentFields\n replies(last: $lastReplies) {\n nodes {\n ...CommentFields\n }\n }\n }": typeof types.DiscussionCommentFieldsFragmentDoc,
22-
"\n fragment DiscussionFields on Discussion {\n number\n title\n stateReason\n isAnswered @include(if: $includeIsAnswered)\n url\n author {\n ...AuthorFields\n }\n comments(last: $lastComments) {\n totalCount\n nodes {\n ...DiscussionCommentFields\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n }\n": typeof types.DiscussionFieldsFragmentDoc,
23-
"\n query fetchDiscussions(\n $queryStatement: String!\n $firstDiscussions: Int\n $lastComments: Int\n $lastReplies: Int\n $firstLabels: Int\n $includeIsAnswered: Boolean!\n ) {\n search(query: $queryStatement, type: DISCUSSION, first: $firstDiscussions) {\n nodes {\n ... on Discussion {\n ...DiscussionFields\n }\n }\n }\n }\n": typeof types.FetchDiscussionsDocument,
20+
"\n query fetchDiscussionByNumber(\n $owner: String!\n $name: String!\n $number: Int!\n $lastComments: Int\n $lastReplies: Int\n $firstLabels: Int\n $includeIsAnswered: Boolean!\n ) {\n repository(owner: $owner, name: $name) {\n discussion(number: $number) {\n __typename\n number\n title\n stateReason\n isAnswered @include(if: $includeIsAnswered)\n url\n author {\n ...AuthorFields\n }\n comments(last: $lastComments) {\n totalCount\n nodes {\n ...CommentFields\n replies(last: $lastReplies) {\n nodes {\n ...CommentFields\n }\n }\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n }\n }\n }\n": typeof types.FetchDiscussionByNumberDocument,
2421
};
2522
const documents: Documents = {
26-
"\n query me {\n viewer {\n name\n }\n }\n ": types.MeDocument,
2723
"\n fragment AuthorFields on Actor {\n login\n url\n avatar_url: avatarUrl\n type: __typename\n }\n": types.AuthorFieldsFragmentDoc,
2824
"\n fragment CommentFields on DiscussionComment {\n databaseId\n createdAt\n author {\n ...AuthorFields\n }\n }\n": types.CommentFieldsFragmentDoc,
29-
"\n fragment DiscussionCommentFields on DiscussionComment {\n ...CommentFields\n replies(last: $lastReplies) {\n nodes {\n ...CommentFields\n }\n }\n }": types.DiscussionCommentFieldsFragmentDoc,
30-
"\n fragment DiscussionFields on Discussion {\n number\n title\n stateReason\n isAnswered @include(if: $includeIsAnswered)\n url\n author {\n ...AuthorFields\n }\n comments(last: $lastComments) {\n totalCount\n nodes {\n ...DiscussionCommentFields\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n }\n": types.DiscussionFieldsFragmentDoc,
31-
"\n query fetchDiscussions(\n $queryStatement: String!\n $firstDiscussions: Int\n $lastComments: Int\n $lastReplies: Int\n $firstLabels: Int\n $includeIsAnswered: Boolean!\n ) {\n search(query: $queryStatement, type: DISCUSSION, first: $firstDiscussions) {\n nodes {\n ... on Discussion {\n ...DiscussionFields\n }\n }\n }\n }\n": types.FetchDiscussionsDocument,
25+
"\n query fetchDiscussionByNumber(\n $owner: String!\n $name: String!\n $number: Int!\n $lastComments: Int\n $lastReplies: Int\n $firstLabels: Int\n $includeIsAnswered: Boolean!\n ) {\n repository(owner: $owner, name: $name) {\n discussion(number: $number) {\n __typename\n number\n title\n stateReason\n isAnswered @include(if: $includeIsAnswered)\n url\n author {\n ...AuthorFields\n }\n comments(last: $lastComments) {\n totalCount\n nodes {\n ...CommentFields\n replies(last: $lastReplies) {\n nodes {\n ...CommentFields\n }\n }\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n }\n }\n }\n": types.FetchDiscussionByNumberDocument,
3226
};
3327

34-
/**
35-
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
36-
*/
37-
export function graphql(source: "\n query me {\n viewer {\n name\n }\n }\n "): typeof import('./graphql').MeDocument;
3828
/**
3929
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
4030
*/
@@ -46,15 +36,7 @@ export function graphql(source: "\n fragment CommentFields on DiscussionComment
4636
/**
4737
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
4838
*/
49-
export function graphql(source: "\n fragment DiscussionCommentFields on DiscussionComment {\n ...CommentFields\n replies(last: $lastReplies) {\n nodes {\n ...CommentFields\n }\n }\n }"): typeof import('./graphql').DiscussionCommentFieldsFragmentDoc;
50-
/**
51-
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
52-
*/
53-
export function graphql(source: "\n fragment DiscussionFields on Discussion {\n number\n title\n stateReason\n isAnswered @include(if: $includeIsAnswered)\n url\n author {\n ...AuthorFields\n }\n comments(last: $lastComments) {\n totalCount\n nodes {\n ...DiscussionCommentFields\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n }\n"): typeof import('./graphql').DiscussionFieldsFragmentDoc;
54-
/**
55-
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
56-
*/
57-
export function graphql(source: "\n query fetchDiscussions(\n $queryStatement: String!\n $firstDiscussions: Int\n $lastComments: Int\n $lastReplies: Int\n $firstLabels: Int\n $includeIsAnswered: Boolean!\n ) {\n search(query: $queryStatement, type: DISCUSSION, first: $firstDiscussions) {\n nodes {\n ... on Discussion {\n ...DiscussionFields\n }\n }\n }\n }\n"): typeof import('./graphql').FetchDiscussionsDocument;
39+
export function graphql(source: "\n query fetchDiscussionByNumber(\n $owner: String!\n $name: String!\n $number: Int!\n $lastComments: Int\n $lastReplies: Int\n $firstLabels: Int\n $includeIsAnswered: Boolean!\n ) {\n repository(owner: $owner, name: $name) {\n discussion(number: $number) {\n __typename\n number\n title\n stateReason\n isAnswered @include(if: $includeIsAnswered)\n url\n author {\n ...AuthorFields\n }\n comments(last: $lastComments) {\n totalCount\n nodes {\n ...CommentFields\n replies(last: $lastReplies) {\n nodes {\n ...CommentFields\n }\n }\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n }\n }\n }\n"): typeof import('./graphql').FetchDiscussionByNumberDocument;
5840

5941

6042
export function graphql(source: string) {

0 commit comments

Comments
 (0)