diff --git a/codegen.ts b/codegen.ts index 40318fb4b..61874aa4d 100644 --- a/codegen.ts +++ b/codegen.ts @@ -22,6 +22,7 @@ const config: CodegenConfig = { }, config: { documentMode: 'string', + enumsAsTypes: true, useTypeImports: true, }, }, diff --git a/src/renderer/__mocks__/notifications-mocks.ts b/src/renderer/__mocks__/notifications-mocks.ts index fe1d45fda..5f8fbff1c 100644 --- a/src/renderer/__mocks__/notifications-mocks.ts +++ b/src/renderer/__mocks__/notifications-mocks.ts @@ -1,9 +1,12 @@ import { Constants } from '../constants'; -import type { AccountNotifications, Hostname } from '../types'; +import type { + AccountNotifications, + GitifyNotificationState, + Hostname, +} from '../types'; import type { Notification, Repository, - StateType, Subject, SubjectType, } from '../typesGitHub'; @@ -43,12 +46,12 @@ export const mockSingleAccountNotifications: AccountNotifications[] = [ export function createMockSubject(mocks: { title?: string; type?: SubjectType; - state?: StateType; + state?: GitifyNotificationState; }): Subject { return { title: mocks.title ?? 'Mock Subject', type: mocks.type ?? ('Unknown' as SubjectType), - state: mocks.state ?? ('Unknown' as StateType), + state: mocks.state ?? ('Unknown' as GitifyNotificationState), url: null, latest_comment_url: null, }; @@ -68,7 +71,15 @@ export function createPartialMockNotification( hasRequiredScopes: true, }, subject: subject as Subject, - repository: repository as Repository, + repository: { + name: 'notifications-test', + full_name: 'gitify-app/notifications-test', + html_url: 'https://github.com/gitify-app/notifications-test', + owner: { + login: 'gitify-app', + }, + ...repository, + } as Repository, }; return mockNotification as Notification; diff --git a/src/renderer/components/metrics/MetricGroup.test.tsx b/src/renderer/components/metrics/MetricGroup.test.tsx index 030c171c4..51089ca65 100644 --- a/src/renderer/components/metrics/MetricGroup.test.tsx +++ b/src/renderer/components/metrics/MetricGroup.test.tsx @@ -1,7 +1,7 @@ import { renderWithAppContext } from '../../__helpers__/test-utils'; import { mockSettings } from '../../__mocks__/state-mocks'; -import type { Milestone } from '../../typesGitHub'; import { mockSingleNotification } from '../../utils/api/__mocks__/response-mocks'; +import type { MilestoneFieldsFragment } from '../../utils/api/graphql/generated/graphql'; import { MetricGroup } from './MetricGroup'; describe('renderer/components/metrics/MetricGroup.tsx', () => { @@ -103,8 +103,8 @@ describe('renderer/components/metrics/MetricGroup.tsx', () => { const mockNotification = mockSingleNotification; mockNotification.subject.milestone = { title: 'Milestone 1', - state: 'open', - } as Milestone; + state: 'OPEN', + } as MilestoneFieldsFragment; const props = { notification: mockNotification, @@ -118,8 +118,8 @@ describe('renderer/components/metrics/MetricGroup.tsx', () => { const mockNotification = mockSingleNotification; mockNotification.subject.milestone = { title: 'Milestone 1', - state: 'closed', - } as Milestone; + state: 'CLOSED', + } as MilestoneFieldsFragment; const props = { notification: mockNotification, diff --git a/src/renderer/components/metrics/MetricGroup.tsx b/src/renderer/components/metrics/MetricGroup.tsx index cdb1bd3d4..fbe14cf7b 100644 --- a/src/renderer/components/metrics/MetricGroup.tsx +++ b/src/renderer/components/metrics/MetricGroup.tsx @@ -84,7 +84,7 @@ export const MetricGroup: FC = ({ {notification.subject.milestone && ( { - return apiRequestAuth(url, 'GET', token); -} - -/** - * Get comments on issues and pull requests. - * Every pull request is an issue, but not every issue is a pull request. - * - * Endpoint documentation: https://docs.github.com/en/rest/issues/comments#get-an-issue-comment - */ -export function getIssueOrPullRequestComment( - url: Link, - token: Token, -): AxiosPromise { - return apiRequestAuth(url, 'GET', token); -} - -/** - * Get details of a pull request. - * - * Endpoint documentation: https://docs.github.com/en/rest/pulls/pulls#get-a-pull-request - */ -export function getPullRequest( - url: Link, - token: Token, -): AxiosPromise { - return apiRequestAuth(url, 'GET', token); -} - -/** - * Lists all reviews for a specified pull request. The list of reviews returns in chronological order. - * - * Endpoint documentation: https://docs.github.com/en/rest/pulls/reviews#list-reviews-for-a-pull-request - */ -export function getPullRequestReviews( - url: Link, - token: Token, -): AxiosPromise { - return apiRequestAuth(url, 'GET', token); -} - /** * Gets a public release with the specified release ID. * @@ -234,10 +188,55 @@ export async function getHtmlUrl(url: Link, token: Token): Promise { } /** - * Search for Discussions that match notification title and repository. - * - * Returns the latest discussion and their latest comments / replies - * + * Fetch GitHub Issue by Issue Number. + */ +export async function fetchIssueByNumber( + notification: Notification, +): Promise> { + const url = getGitHubGraphQLUrl(notification.account.hostname); + const number = getNumberFromUrl(notification.subject.url); + + return performGraphQLRequest( + url.toString() as Link, + notification.account.token, + FetchIssueByNumberDocument, + { + owner: notification.repository.owner.login, + name: notification.repository.name, + number: number, + firstLabels: 100, + lastComments: 1, + }, + ); +} + +/** + * Fetch GitHub Pull Request by PR Number. + */ +export async function fetchPullByNumber( + notification: Notification, +): Promise> { + const url = getGitHubGraphQLUrl(notification.account.hostname); + const number = getNumberFromUrl(notification.subject.url); + + return performGraphQLRequest( + url.toString() as Link, + notification.account.token, + FetchPullRequestByNumberDocument, + { + owner: notification.repository.owner.login, + name: notification.repository.name, + number: number, + firstLabels: 100, + firstClosingIssues: 100, + lastComments: 1, + lastReviews: 100, + }, + ); +} + +/** + * Fetch GitHub Discussion by Discussion Number. */ export async function fetchDiscussionByNumber( notification: Notification, diff --git a/src/renderer/utils/api/graphql/common.graphql b/src/renderer/utils/api/graphql/common.graphql new file mode 100644 index 000000000..afb3c4151 --- /dev/null +++ b/src/renderer/utils/api/graphql/common.graphql @@ -0,0 +1,6 @@ +fragment AuthorFields on Actor { + login + html_url: url + avatar_url: avatarUrl + type: __typename +} diff --git a/src/renderer/utils/api/graphql/discussions.graphql b/src/renderer/utils/api/graphql/discussion.graphql similarity index 90% rename from src/renderer/utils/api/graphql/discussions.graphql rename to src/renderer/utils/api/graphql/discussion.graphql index 92c3c7f06..2dbe24e1a 100644 --- a/src/renderer/utils/api/graphql/discussions.graphql +++ b/src/renderer/utils/api/graphql/discussion.graphql @@ -23,6 +23,7 @@ query FetchDiscussionByNumber( nodes { ...CommentFields replies(last: $lastReplies) { + totalCount nodes { ...CommentFields } @@ -38,17 +39,11 @@ query FetchDiscussionByNumber( } } -fragment AuthorFields on Actor { - login - url - avatar_url: avatarUrl - type: __typename -} - fragment CommentFields on DiscussionComment { databaseId createdAt author { ...AuthorFields } + url } diff --git a/src/renderer/utils/api/graphql/generated/gql.ts b/src/renderer/utils/api/graphql/generated/gql.ts index 1a24e20d9..1e0234ae7 100644 --- a/src/renderer/utils/api/graphql/generated/gql.ts +++ b/src/renderer/utils/api/graphql/generated/gql.ts @@ -15,16 +15,34 @@ import * as types from './graphql'; * Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size */ type Documents = { - "query FetchDiscussionByNumber($owner: String!, $name: String!, $number: Int!, $lastComments: Int, $lastReplies: Int, $firstLabels: Int, $includeIsAnswered: Boolean!) {\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\nfragment AuthorFields on Actor {\n login\n url\n avatar_url: avatarUrl\n type: __typename\n}\n\nfragment CommentFields on DiscussionComment {\n databaseId\n createdAt\n author {\n ...AuthorFields\n }\n}": typeof types.FetchDiscussionByNumberDocument, + "fragment AuthorFields on Actor {\n login\n html_url: url\n avatar_url: avatarUrl\n type: __typename\n}": typeof types.AuthorFieldsFragmentDoc, + "query FetchDiscussionByNumber($owner: String!, $name: String!, $number: Int!, $lastComments: Int, $lastReplies: Int, $firstLabels: Int, $includeIsAnswered: Boolean!) {\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 totalCount\n nodes {\n ...CommentFields\n }\n }\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n }\n }\n}\n\nfragment CommentFields on DiscussionComment {\n databaseId\n createdAt\n author {\n ...AuthorFields\n }\n url\n}": typeof types.FetchDiscussionByNumberDocument, + "query FetchIssueByNumber($owner: String!, $name: String!, $number: Int!, $lastComments: Int, $firstLabels: Int) {\n repository(owner: $owner, name: $name) {\n issue(number: $number) {\n __typename\n number\n title\n url\n state\n stateReason\n milestone {\n ...MilestoneFields\n }\n author {\n ...AuthorFields\n }\n comments(last: $lastComments) {\n totalCount\n nodes {\n url\n author {\n ...AuthorFields\n }\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n }\n }\n}\n\nfragment MilestoneFields on Milestone {\n state\n title\n}": typeof types.FetchIssueByNumberDocument, + "query FetchPullRequestByNumber($owner: String!, $name: String!, $number: Int!, $firstLabels: Int, $lastComments: Int, $lastReviews: Int, $firstClosingIssues: Int) {\n repository(owner: $owner, name: $name) {\n pullRequest(number: $number) {\n __typename\n number\n title\n url\n state\n merged\n isDraft\n isInMergeQueue\n milestone {\n ...MilestoneFields\n }\n author {\n ...AuthorFields\n }\n comments(last: $lastComments) {\n totalCount\n nodes {\n url\n author {\n ...AuthorFields\n }\n }\n }\n reviews(last: $lastReviews) {\n totalCount\n nodes {\n state\n author {\n login\n }\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n closingIssuesReferences(first: $firstClosingIssues) {\n nodes {\n number\n }\n }\n }\n }\n}\n\nfragment MilestoneFields on Milestone {\n state\n title\n}": typeof types.FetchPullRequestByNumberDocument, }; const documents: Documents = { - "query FetchDiscussionByNumber($owner: String!, $name: String!, $number: Int!, $lastComments: Int, $lastReplies: Int, $firstLabels: Int, $includeIsAnswered: Boolean!) {\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\nfragment AuthorFields on Actor {\n login\n url\n avatar_url: avatarUrl\n type: __typename\n}\n\nfragment CommentFields on DiscussionComment {\n databaseId\n createdAt\n author {\n ...AuthorFields\n }\n}": types.FetchDiscussionByNumberDocument, + "fragment AuthorFields on Actor {\n login\n html_url: url\n avatar_url: avatarUrl\n type: __typename\n}": types.AuthorFieldsFragmentDoc, + "query FetchDiscussionByNumber($owner: String!, $name: String!, $number: Int!, $lastComments: Int, $lastReplies: Int, $firstLabels: Int, $includeIsAnswered: Boolean!) {\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 totalCount\n nodes {\n ...CommentFields\n }\n }\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n }\n }\n}\n\nfragment CommentFields on DiscussionComment {\n databaseId\n createdAt\n author {\n ...AuthorFields\n }\n url\n}": types.FetchDiscussionByNumberDocument, + "query FetchIssueByNumber($owner: String!, $name: String!, $number: Int!, $lastComments: Int, $firstLabels: Int) {\n repository(owner: $owner, name: $name) {\n issue(number: $number) {\n __typename\n number\n title\n url\n state\n stateReason\n milestone {\n ...MilestoneFields\n }\n author {\n ...AuthorFields\n }\n comments(last: $lastComments) {\n totalCount\n nodes {\n url\n author {\n ...AuthorFields\n }\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n }\n }\n}\n\nfragment MilestoneFields on Milestone {\n state\n title\n}": types.FetchIssueByNumberDocument, + "query FetchPullRequestByNumber($owner: String!, $name: String!, $number: Int!, $firstLabels: Int, $lastComments: Int, $lastReviews: Int, $firstClosingIssues: Int) {\n repository(owner: $owner, name: $name) {\n pullRequest(number: $number) {\n __typename\n number\n title\n url\n state\n merged\n isDraft\n isInMergeQueue\n milestone {\n ...MilestoneFields\n }\n author {\n ...AuthorFields\n }\n comments(last: $lastComments) {\n totalCount\n nodes {\n url\n author {\n ...AuthorFields\n }\n }\n }\n reviews(last: $lastReviews) {\n totalCount\n nodes {\n state\n author {\n login\n }\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n closingIssuesReferences(first: $firstClosingIssues) {\n nodes {\n number\n }\n }\n }\n }\n}\n\nfragment MilestoneFields on Milestone {\n state\n title\n}": types.FetchPullRequestByNumberDocument, }; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query FetchDiscussionByNumber($owner: String!, $name: String!, $number: Int!, $lastComments: Int, $lastReplies: Int, $firstLabels: Int, $includeIsAnswered: Boolean!) {\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\nfragment AuthorFields on Actor {\n login\n url\n avatar_url: avatarUrl\n type: __typename\n}\n\nfragment CommentFields on DiscussionComment {\n databaseId\n createdAt\n author {\n ...AuthorFields\n }\n}"): typeof import('./graphql').FetchDiscussionByNumberDocument; +export function graphql(source: "fragment AuthorFields on Actor {\n login\n html_url: url\n avatar_url: avatarUrl\n type: __typename\n}"): typeof import('./graphql').AuthorFieldsFragmentDoc; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "query FetchDiscussionByNumber($owner: String!, $name: String!, $number: Int!, $lastComments: Int, $lastReplies: Int, $firstLabels: Int, $includeIsAnswered: Boolean!) {\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 totalCount\n nodes {\n ...CommentFields\n }\n }\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n }\n }\n}\n\nfragment CommentFields on DiscussionComment {\n databaseId\n createdAt\n author {\n ...AuthorFields\n }\n url\n}"): typeof import('./graphql').FetchDiscussionByNumberDocument; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "query FetchIssueByNumber($owner: String!, $name: String!, $number: Int!, $lastComments: Int, $firstLabels: Int) {\n repository(owner: $owner, name: $name) {\n issue(number: $number) {\n __typename\n number\n title\n url\n state\n stateReason\n milestone {\n ...MilestoneFields\n }\n author {\n ...AuthorFields\n }\n comments(last: $lastComments) {\n totalCount\n nodes {\n url\n author {\n ...AuthorFields\n }\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n }\n }\n}\n\nfragment MilestoneFields on Milestone {\n state\n title\n}"): typeof import('./graphql').FetchIssueByNumberDocument; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "query FetchPullRequestByNumber($owner: String!, $name: String!, $number: Int!, $firstLabels: Int, $lastComments: Int, $lastReviews: Int, $firstClosingIssues: Int) {\n repository(owner: $owner, name: $name) {\n pullRequest(number: $number) {\n __typename\n number\n title\n url\n state\n merged\n isDraft\n isInMergeQueue\n milestone {\n ...MilestoneFields\n }\n author {\n ...AuthorFields\n }\n comments(last: $lastComments) {\n totalCount\n nodes {\n url\n author {\n ...AuthorFields\n }\n }\n }\n reviews(last: $lastReviews) {\n totalCount\n nodes {\n state\n author {\n login\n }\n }\n }\n labels(first: $firstLabels) {\n nodes {\n name\n }\n }\n closingIssuesReferences(first: $firstClosingIssues) {\n nodes {\n number\n }\n }\n }\n }\n}\n\nfragment MilestoneFields on Milestone {\n state\n title\n}"): typeof import('./graphql').FetchPullRequestByNumberDocument; export function graphql(source: string) { diff --git a/src/renderer/utils/api/graphql/generated/graphql.ts b/src/renderer/utils/api/graphql/generated/graphql.ts index 4ac16af25..89a34f8ef 100644 --- a/src/renderer/utils/api/graphql/generated/graphql.ts +++ b/src/renderer/utils/api/graphql/generated/graphql.ts @@ -228,12 +228,11 @@ export type ActorLocation = { }; /** The actor's type. */ -export enum ActorType { +export type ActorType = /** Indicates a team actor. */ - Team = 'TEAM', + | 'TEAM' /** Indicates a user actor. */ - User = 'USER' -} + | 'USER'; /** Autogenerated input type of AddAssigneesToAssignable */ export type AddAssigneesToAssignableInput = { @@ -1123,10 +1122,9 @@ export type AuditLogOrder = { }; /** Properties by which Audit Log connections can be ordered. */ -export enum AuditLogOrderField { +export type AuditLogOrderField = /** Order audit log entries by timestamp */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** Represents a 'auto_merge_disabled' event on a given pull request. */ export type AutoMergeDisabledEvent = Node & { @@ -1900,14 +1898,13 @@ export type CheckAnnotationEdge = { }; /** Represents an annotation's information level. */ -export enum CheckAnnotationLevel { +export type CheckAnnotationLevel = /** An annotation indicating an inescapable error. */ - Failure = 'FAILURE', + | 'FAILURE' /** An annotation indicating some information. */ - Notice = 'NOTICE', + | 'NOTICE' /** An annotation indicating an ignorable error. */ - Warning = 'WARNING' -} + | 'WARNING'; /** A character position in a check annotation. */ export type CheckAnnotationPosition = { @@ -1940,26 +1937,25 @@ export type CheckAnnotationSpan = { }; /** The possible states for a check suite or run conclusion. */ -export enum CheckConclusionState { +export type CheckConclusionState = /** The check suite or run requires action. */ - ActionRequired = 'ACTION_REQUIRED', + | 'ACTION_REQUIRED' /** The check suite or run has been cancelled. */ - Cancelled = 'CANCELLED', + | 'CANCELLED' /** The check suite or run has failed. */ - Failure = 'FAILURE', + | 'FAILURE' /** The check suite or run was neutral. */ - Neutral = 'NEUTRAL', + | 'NEUTRAL' /** The check suite or run was skipped. */ - Skipped = 'SKIPPED', + | 'SKIPPED' /** The check suite or run was marked stale by GitHub. Only GitHub can use this conclusion. */ - Stale = 'STALE', + | 'STALE' /** The check suite or run has failed at startup. */ - StartupFailure = 'STARTUP_FAILURE', + | 'STARTUP_FAILURE' /** The check suite or run has succeeded. */ - Success = 'SUCCESS', + | 'SUCCESS' /** The check suite or run has timed out. */ - TimedOut = 'TIMED_OUT' -} + | 'TIMED_OUT'; /** A check run. */ export type CheckRun = Node & RequirableByPullRequest & UniformResourceLocatable & { @@ -2109,36 +2105,35 @@ export type CheckRunOutputImage = { }; /** The possible states of a check run in a status rollup. */ -export enum CheckRunState { +export type CheckRunState = /** The check run requires action. */ - ActionRequired = 'ACTION_REQUIRED', + | 'ACTION_REQUIRED' /** The check run has been cancelled. */ - Cancelled = 'CANCELLED', + | 'CANCELLED' /** The check run has been completed. */ - Completed = 'COMPLETED', + | 'COMPLETED' /** The check run has failed. */ - Failure = 'FAILURE', + | 'FAILURE' /** The check run is in progress. */ - InProgress = 'IN_PROGRESS', + | 'IN_PROGRESS' /** The check run was neutral. */ - Neutral = 'NEUTRAL', + | 'NEUTRAL' /** The check run is in pending state. */ - Pending = 'PENDING', + | 'PENDING' /** The check run has been queued. */ - Queued = 'QUEUED', + | 'QUEUED' /** The check run was skipped. */ - Skipped = 'SKIPPED', + | 'SKIPPED' /** The check run was marked stale by GitHub. Only GitHub can use this conclusion. */ - Stale = 'STALE', + | 'STALE' /** The check run has failed at startup. */ - StartupFailure = 'STARTUP_FAILURE', + | 'STARTUP_FAILURE' /** The check run has succeeded. */ - Success = 'SUCCESS', + | 'SUCCESS' /** The check run has timed out. */ - TimedOut = 'TIMED_OUT', + | 'TIMED_OUT' /** The check run is in waiting state. */ - Waiting = 'WAITING' -} + | 'WAITING'; /** Represents a count of the state of a check run. */ export type CheckRunStateCount = { @@ -2150,28 +2145,26 @@ export type CheckRunStateCount = { }; /** The possible types of check runs. */ -export enum CheckRunType { +export type CheckRunType = /** Every check run available. */ - All = 'ALL', + | 'ALL' /** The latest check run. */ - Latest = 'LATEST' -} + | 'LATEST'; /** The possible states for a check suite or run status. */ -export enum CheckStatusState { +export type CheckStatusState = /** The check suite or run has been completed. */ - Completed = 'COMPLETED', + | 'COMPLETED' /** The check suite or run is in progress. */ - InProgress = 'IN_PROGRESS', + | 'IN_PROGRESS' /** The check suite or run is in pending state. */ - Pending = 'PENDING', + | 'PENDING' /** The check suite or run has been queued. */ - Queued = 'QUEUED', + | 'QUEUED' /** The check suite or run has been requested. */ - Requested = 'REQUESTED', + | 'REQUESTED' /** The check suite or run is in waiting state. */ - Waiting = 'WAITING' -} + | 'WAITING'; /** A single check step. */ export type CheckStep = { @@ -2561,14 +2554,13 @@ export type CodeScanningToolInput = { }; /** Collaborators affiliation level with a subject. */ -export enum CollaboratorAffiliation { +export type CollaboratorAffiliation = /** All collaborators the authenticated user can see. */ - All = 'ALL', + | 'ALL' /** All collaborators with permissions to an organization-owned subject, regardless of organization membership status. */ - Direct = 'DIRECT', + | 'DIRECT' /** All outside collaborators of an organization-owned subject. */ - Outside = 'OUTSIDE' -} + | 'OUTSIDE'; /** Represents a comment. */ export type Comment = { @@ -2614,42 +2606,40 @@ export type CommentUserContentEditsArgs = { }; /** A comment author association with repository. */ -export enum CommentAuthorAssociation { +export type CommentAuthorAssociation = /** Author has been invited to collaborate on the repository. */ - Collaborator = 'COLLABORATOR', + | 'COLLABORATOR' /** Author has previously committed to the repository. */ - Contributor = 'CONTRIBUTOR', + | 'CONTRIBUTOR' /** Author has not previously committed to GitHub. */ - FirstTimer = 'FIRST_TIMER', + | 'FIRST_TIMER' /** Author has not previously committed to the repository. */ - FirstTimeContributor = 'FIRST_TIME_CONTRIBUTOR', + | 'FIRST_TIME_CONTRIBUTOR' /** Author is a placeholder for an unclaimed user. */ - Mannequin = 'MANNEQUIN', + | 'MANNEQUIN' /** Author is a member of the organization that owns the repository. */ - Member = 'MEMBER', + | 'MEMBER' /** Author has no association with the repository. */ - None = 'NONE', + | 'NONE' /** Author is the owner of the repository. */ - Owner = 'OWNER' -} + | 'OWNER'; /** The possible errors that will prevent a user from updating a comment. */ -export enum CommentCannotUpdateReason { +export type CommentCannotUpdateReason = /** Unable to create comment because repository is archived. */ - Archived = 'ARCHIVED', + | 'ARCHIVED' /** You cannot update this comment */ - Denied = 'DENIED', + | 'DENIED' /** You must be the author or have write access to this repository to update this comment. */ - InsufficientAccess = 'INSUFFICIENT_ACCESS', + | 'INSUFFICIENT_ACCESS' /** Unable to create comment because issue is locked. */ - Locked = 'LOCKED', + | 'LOCKED' /** You must be logged in to update this comment. */ - LoginRequired = 'LOGIN_REQUIRED', + | 'LOGIN_REQUIRED' /** Repository is under maintenance. */ - Maintenance = 'MAINTENANCE', + | 'MAINTENANCE' /** At least one email address must be verified to update this comment. */ - VerifiedEmailRequired = 'VERIFIED_EMAIL_REQUIRED' -} + | 'VERIFIED_EMAIL_REQUIRED'; /** Represents a 'comment_deleted' event on a given issue or pull request. */ export type CommentDeletedEvent = Node & { @@ -3061,12 +3051,11 @@ export type CommitContributionOrder = { }; /** Properties by which commit contribution connections can be ordered. */ -export enum CommitContributionOrderField { +export type CommitContributionOrderField = /** Order commit contributions by how many commits they represent. */ - CommitCount = 'COMMIT_COUNT', + | 'COMMIT_COUNT' /** Order commit contributions by when they were made. */ - OccurredAt = 'OCCURRED_AT' -} + | 'OCCURRED_AT'; /** This aggregates commits made by a user within one repository. */ export type CommitContributionsByRepository = { @@ -3249,16 +3238,15 @@ export type ComparisonCommitConnection = { }; /** The status of a git comparison between two refs. */ -export enum ComparisonStatus { +export type ComparisonStatus = /** The head ref is ahead of the base ref. */ - Ahead = 'AHEAD', + | 'AHEAD' /** The head ref is behind the base ref. */ - Behind = 'BEHIND', + | 'BEHIND' /** The head ref is both ahead and behind of the base ref, indicating git history has diverged. */ - Diverged = 'DIVERGED', + | 'DIVERGED' /** The head ref and base ref are identical. */ - Identical = 'IDENTICAL' -} + | 'IDENTICAL'; /** Represents a 'connected' event on a given issue or pull request. */ export type ConnectedEvent = Node & { @@ -3363,18 +3351,17 @@ export type ContributionCalendarWeek = { }; /** Varying levels of contributions from none to many. */ -export enum ContributionLevel { +export type ContributionLevel = /** Lowest 25% of days of contributions. */ - FirstQuartile = 'FIRST_QUARTILE', + | 'FIRST_QUARTILE' /** Highest 25% of days of contributions. More contributions than the third quartile. */ - FourthQuartile = 'FOURTH_QUARTILE', + | 'FOURTH_QUARTILE' /** No contributions occurred. */ - None = 'NONE', + | 'NONE' /** Second lowest 25% of days of contributions. More contributions than the first quartile. */ - SecondQuartile = 'SECOND_QUARTILE', + | 'SECOND_QUARTILE' /** Second highest 25% of days of contributions. More contributions than second quartile, less than the fourth quartile. */ - ThirdQuartile = 'THIRD_QUARTILE' -} + | 'THIRD_QUARTILE'; /** Ordering options for contribution connections. */ export type ContributionOrder = { @@ -5042,18 +5029,17 @@ export type CustomPropertyValueInput = { }; /** The allowed value types for a custom property definition. */ -export enum CustomPropertyValueType { +export type CustomPropertyValueType = /** A multi-select value. */ - MultiSelect = 'MULTI_SELECT', + | 'MULTI_SELECT' /** A single-select value. */ - SingleSelect = 'SINGLE_SELECT', + | 'SINGLE_SELECT' /** A string value. */ - String = 'STRING', + | 'STRING' /** A true/false value. */ - TrueFalse = 'TRUE_FALSE', + | 'TRUE_FALSE' /** A URL value. */ - Url = 'URL' -} + | 'URL'; /** The Common Vulnerability Scoring System */ export type CvssSeverities = { @@ -5110,16 +5096,15 @@ export type DeclineTopicSuggestionPayload = { }; /** The possible base permissions for repositories. */ -export enum DefaultRepositoryPermissionField { +export type DefaultRepositoryPermissionField = /** Can read, write, and administrate repos by default */ - Admin = 'ADMIN', + | 'ADMIN' /** No access */ - None = 'NONE', + | 'NONE' /** Can read repos by default */ - Read = 'READ', + | 'READ' /** Can read and write repos by default */ - Write = 'WRITE' -} + | 'WRITE'; /** Entities that can be deleted. */ export type Deletable = { @@ -5683,30 +5668,29 @@ export type DependencyGraphDependencyEdge = { }; /** The possible ecosystems of a dependency graph package. */ -export enum DependencyGraphEcosystem { +export type DependencyGraphEcosystem = /** GitHub Actions */ - Actions = 'ACTIONS', + | 'ACTIONS' /** PHP packages hosted at packagist.org */ - Composer = 'COMPOSER', + | 'COMPOSER' /** Go modules */ - Go = 'GO', + | 'GO' /** Java artifacts hosted at the Maven central repository */ - Maven = 'MAVEN', + | 'MAVEN' /** JavaScript packages hosted at npmjs.com */ - Npm = 'NPM', + | 'NPM' /** .NET packages hosted at the NuGet Gallery */ - Nuget = 'NUGET', + | 'NUGET' /** Python packages hosted at PyPI.org */ - Pip = 'PIP', + | 'PIP' /** Dart packages hosted at pub.dev */ - Pub = 'PUB', + | 'PUB' /** Ruby gems hosted at RubyGems.org */ - Rubygems = 'RUBYGEMS', + | 'RUBYGEMS' /** Rust crates */ - Rust = 'RUST', + | 'RUST' /** Swift packages */ - Swift = 'SWIFT' -} + | 'SWIFT'; /** Dependency manifest for a repository */ export type DependencyGraphManifest = Node & { @@ -5916,10 +5900,9 @@ export type DeploymentOrder = { }; /** Properties by which deployment connections can be ordered. */ -export enum DeploymentOrderField { +export type DeploymentOrderField = /** Order collection by creation time */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** A protection rule. */ export type DeploymentProtectionRule = { @@ -5968,14 +5951,13 @@ export type DeploymentProtectionRuleEdge = { }; /** The possible protection rule types. */ -export enum DeploymentProtectionRuleType { +export type DeploymentProtectionRuleType = /** Branch policy */ - BranchPolicy = 'BRANCH_POLICY', + | 'BRANCH_POLICY' /** Required reviewers */ - RequiredReviewers = 'REQUIRED_REVIEWERS', + | 'REQUIRED_REVIEWERS' /** Wait timer */ - WaitTimer = 'WAIT_TIMER' -} + | 'WAIT_TIMER'; /** A request to deploy a workflow run to an environment. */ export type DeploymentRequest = { @@ -6072,12 +6054,11 @@ export type DeploymentReviewEdge = { }; /** The possible states for a deployment review. */ -export enum DeploymentReviewState { +export type DeploymentReviewState = /** The deployment was approved. */ - Approved = 'APPROVED', + | 'APPROVED' /** The deployment was rejected. */ - Rejected = 'REJECTED' -} + | 'REJECTED'; /** Users and teams. */ export type DeploymentReviewer = Team | User; @@ -6105,30 +6086,29 @@ export type DeploymentReviewerEdge = { }; /** The possible states in which a deployment can be. */ -export enum DeploymentState { +export type DeploymentState = /** The pending deployment was not updated after 30 minutes. */ - Abandoned = 'ABANDONED', + | 'ABANDONED' /** The deployment is currently active. */ - Active = 'ACTIVE', + | 'ACTIVE' /** An inactive transient deployment. */ - Destroyed = 'DESTROYED', + | 'DESTROYED' /** The deployment experienced an error. */ - Error = 'ERROR', + | 'ERROR' /** The deployment has failed. */ - Failure = 'FAILURE', + | 'FAILURE' /** The deployment is inactive. */ - Inactive = 'INACTIVE', + | 'INACTIVE' /** The deployment is in progress. */ - InProgress = 'IN_PROGRESS', + | 'IN_PROGRESS' /** The deployment is pending. */ - Pending = 'PENDING', + | 'PENDING' /** The deployment has queued */ - Queued = 'QUEUED', + | 'QUEUED' /** The deployment was successful. */ - Success = 'SUCCESS', + | 'SUCCESS' /** The deployment is waiting. */ - Waiting = 'WAITING' -} + | 'WAITING'; /** Describes the status of a given deployment attempt. */ export type DeploymentStatus = Node & { @@ -6178,24 +6158,23 @@ export type DeploymentStatusEdge = { }; /** The possible states for a deployment status. */ -export enum DeploymentStatusState { +export type DeploymentStatusState = /** The deployment experienced an error. */ - Error = 'ERROR', + | 'ERROR' /** The deployment has failed. */ - Failure = 'FAILURE', + | 'FAILURE' /** The deployment is inactive. */ - Inactive = 'INACTIVE', + | 'INACTIVE' /** The deployment is in progress. */ - InProgress = 'IN_PROGRESS', + | 'IN_PROGRESS' /** The deployment is pending. */ - Pending = 'PENDING', + | 'PENDING' /** The deployment is queued */ - Queued = 'QUEUED', + | 'QUEUED' /** The deployment was successful. */ - Success = 'SUCCESS', + | 'SUCCESS' /** The deployment is waiting. */ - Waiting = 'WAITING' -} + | 'WAITING'; /** Autogenerated input type of DequeuePullRequest */ export type DequeuePullRequestInput = { @@ -6215,12 +6194,11 @@ export type DequeuePullRequestPayload = { }; /** The possible sides of a diff. */ -export enum DiffSide { +export type DiffSide = /** The left side of the diff. */ - Left = 'LEFT', + | 'LEFT' /** The right side of the diff. */ - Right = 'RIGHT' -} + | 'RIGHT'; /** Autogenerated input type of DisablePullRequestAutoMerge */ export type DisablePullRequestAutoMergeInput = { @@ -6444,14 +6422,13 @@ export type DiscussionCategoryEdge = { }; /** The possible reasons for closing a discussion. */ -export enum DiscussionCloseReason { +export type DiscussionCloseReason = /** The discussion is a duplicate of another */ - Duplicate = 'DUPLICATE', + | 'DUPLICATE' /** The discussion is no longer relevant */ - Outdated = 'OUTDATED', + | 'OUTDATED' /** The discussion has been resolved */ - Resolved = 'RESOLVED' -} + | 'RESOLVED'; /** A comment on a discussion. */ export type DiscussionComment = Comment & Deletable & Minimizable & Node & Reactable & Updatable & UpdatableComment & Votable & { @@ -6616,12 +6593,11 @@ export type DiscussionOrder = { }; /** Properties by which discussion connections can be ordered. */ -export enum DiscussionOrderField { +export type DiscussionOrderField = /** Order discussions by creation time. */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order discussions by most recent modification time. */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** A poll for a discussion. */ export type DiscussionPoll = Node & { @@ -6698,32 +6674,29 @@ export type DiscussionPollOptionOrder = { }; /** Properties by which discussion poll option connections can be ordered. */ -export enum DiscussionPollOptionOrderField { +export type DiscussionPollOptionOrderField = /** Order poll options by the order that the poll author specified when creating the poll. */ - AuthoredOrder = 'AUTHORED_ORDER', + | 'AUTHORED_ORDER' /** Order poll options by the number of votes it has. */ - VoteCount = 'VOTE_COUNT' -} + | 'VOTE_COUNT'; /** The possible states of a discussion. */ -export enum DiscussionState { +export type DiscussionState = /** A discussion that has been closed */ - Closed = 'CLOSED', + | 'CLOSED' /** A discussion that is open */ - Open = 'OPEN' -} + | 'OPEN'; /** The possible state reasons of a discussion. */ -export enum DiscussionStateReason { +export type DiscussionStateReason = /** The discussion is a duplicate of another */ - Duplicate = 'DUPLICATE', + | 'DUPLICATE' /** The discussion is no longer relevant */ - Outdated = 'OUTDATED', + | 'OUTDATED' /** The discussion was reopened */ - Reopened = 'REOPENED', + | 'REOPENED' /** The discussion has been resolved */ - Resolved = 'RESOLVED' -} + | 'RESOLVED'; /** Autogenerated input type of DismissPullRequestReview */ export type DismissPullRequestReviewInput = { @@ -6745,18 +6718,17 @@ export type DismissPullRequestReviewPayload = { }; /** The possible reasons that a Dependabot alert was dismissed. */ -export enum DismissReason { +export type DismissReason = /** A fix has already been started */ - FixStarted = 'FIX_STARTED', + | 'FIX_STARTED' /** This alert is inaccurate or incorrect */ - Inaccurate = 'INACCURATE', + | 'INACCURATE' /** Vulnerable code is not actually used */ - NotUsed = 'NOT_USED', + | 'NOT_USED' /** No bandwidth to fix this */ - NoBandwidth = 'NO_BANDWIDTH', + | 'NO_BANDWIDTH' /** Risk is tolerable to this project */ - TolerableRisk = 'TOLERABLE_RISK' -} + | 'TOLERABLE_RISK'; /** Autogenerated input type of DismissRepositoryVulnerabilityAlert */ export type DismissRepositoryVulnerabilityAlertInput = { @@ -7126,36 +7098,33 @@ export type EnterpriseAdministratorInvitationOrder = { }; /** Properties by which enterprise administrator invitation connections can be ordered. */ -export enum EnterpriseAdministratorInvitationOrderField { +export type EnterpriseAdministratorInvitationOrderField = /** Order enterprise administrator member invitations by creation time */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** The possible administrator roles in an enterprise account. */ -export enum EnterpriseAdministratorRole { +export type EnterpriseAdministratorRole = /** Represents a billing manager of the enterprise account. */ - BillingManager = 'BILLING_MANAGER', + | 'BILLING_MANAGER' /** Represents an owner of the enterprise account. */ - Owner = 'OWNER', + | 'OWNER' /** Unaffiliated member of the enterprise account without an admin role. */ - Unaffiliated = 'UNAFFILIATED' -} + | 'UNAFFILIATED'; /** The possible values for the enterprise allow private repository forking policy value. */ -export enum EnterpriseAllowPrivateRepositoryForkingPolicyValue { +export type EnterpriseAllowPrivateRepositoryForkingPolicyValue = /** Members can fork a repository to an organization within this enterprise. */ - EnterpriseOrganizations = 'ENTERPRISE_ORGANIZATIONS', + | 'ENTERPRISE_ORGANIZATIONS' /** Members can fork a repository to their enterprise-managed user account or an organization inside this enterprise. */ - EnterpriseOrganizationsUserAccounts = 'ENTERPRISE_ORGANIZATIONS_USER_ACCOUNTS', + | 'ENTERPRISE_ORGANIZATIONS_USER_ACCOUNTS' /** Members can fork a repository to their user account or an organization, either inside or outside of this enterprise. */ - Everywhere = 'EVERYWHERE', + | 'EVERYWHERE' /** Members can fork a repository only within the same organization (intra-org). */ - SameOrganization = 'SAME_ORGANIZATION', + | 'SAME_ORGANIZATION' /** Members can fork a repository to their user account or within the same organization. */ - SameOrganizationUserAccounts = 'SAME_ORGANIZATION_USER_ACCOUNTS', + | 'SAME_ORGANIZATION_USER_ACCOUNTS' /** Members can fork a repository to their user account. */ - UserAccounts = 'USER_ACCOUNTS' -} + | 'USER_ACCOUNTS'; /** Metadata for an audit entry containing enterprise account information. */ export type EnterpriseAuditEntryData = { @@ -7206,26 +7175,24 @@ export type EnterpriseConnection = { }; /** The possible values for the enterprise base repository permission setting. */ -export enum EnterpriseDefaultRepositoryPermissionSettingValue { +export type EnterpriseDefaultRepositoryPermissionSettingValue = /** Organization members will be able to clone, pull, push, and add new collaborators to all organization repositories. */ - Admin = 'ADMIN', + | 'ADMIN' /** Organization members will only be able to clone and pull public repositories. */ - None = 'NONE', + | 'NONE' /** Organizations in the enterprise choose base repository permissions for their members. */ - NoPolicy = 'NO_POLICY', + | 'NO_POLICY' /** Organization members will be able to clone and pull all organization repositories. */ - Read = 'READ', + | 'READ' /** Organization members will be able to clone, pull, and push all organization repositories. */ - Write = 'WRITE' -} + | 'WRITE'; /** The possible values for an enabled/no policy enterprise setting. */ -export enum EnterpriseDisallowedMethodsSettingValue { +export type EnterpriseDisallowedMethodsSettingValue = /** The setting prevents insecure 2FA methods from being used by members of the enterprise. */ - Insecure = 'INSECURE', + | 'INSECURE' /** There is no policy set for preventing insecure 2FA methods from being used by members of the enterprise. */ - NoPolicy = 'NO_POLICY' -} + | 'NO_POLICY'; /** An edge in a connection. */ export type EnterpriseEdge = { @@ -7237,22 +7204,20 @@ export type EnterpriseEdge = { }; /** The possible values for an enabled/disabled enterprise setting. */ -export enum EnterpriseEnabledDisabledSettingValue { +export type EnterpriseEnabledDisabledSettingValue = /** The setting is disabled for organizations in the enterprise. */ - Disabled = 'DISABLED', + | 'DISABLED' /** The setting is enabled for organizations in the enterprise. */ - Enabled = 'ENABLED', + | 'ENABLED' /** There is no policy set for organizations in the enterprise. */ - NoPolicy = 'NO_POLICY' -} + | 'NO_POLICY'; /** The possible values for an enabled/no policy enterprise setting. */ -export enum EnterpriseEnabledSettingValue { +export type EnterpriseEnabledSettingValue = /** The setting is enabled for organizations in the enterprise. */ - Enabled = 'ENABLED', + | 'ENABLED' /** There is no policy set for organizations in the enterprise. */ - NoPolicy = 'NO_POLICY' -} + | 'NO_POLICY'; /** The connection type for OrganizationInvitation. */ export type EnterpriseFailedInvitationConnection = { @@ -7386,10 +7351,9 @@ export type EnterpriseMemberInvitationOrder = { }; /** Properties by which enterprise member invitation connections can be ordered. */ -export enum EnterpriseMemberInvitationOrderField { +export type EnterpriseMemberInvitationOrderField = /** Order enterprise member invitations by creation time */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** Ordering options for enterprise member connections. */ export type EnterpriseMemberOrder = { @@ -7400,46 +7364,42 @@ export type EnterpriseMemberOrder = { }; /** Properties by which enterprise member connections can be ordered. */ -export enum EnterpriseMemberOrderField { +export type EnterpriseMemberOrderField = /** Order enterprise members by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order enterprise members by login */ - Login = 'LOGIN' -} + | 'LOGIN'; /** The possible values for the enterprise members can create repositories setting. */ -export enum EnterpriseMembersCanCreateRepositoriesSettingValue { +export type EnterpriseMembersCanCreateRepositoriesSettingValue = /** Members will be able to create public and private repositories. */ - All = 'ALL', + | 'ALL' /** Members will not be able to create public or private repositories. */ - Disabled = 'DISABLED', + | 'DISABLED' /** Organization owners choose whether to allow members to create repositories. */ - NoPolicy = 'NO_POLICY', + | 'NO_POLICY' /** Members will be able to create only private repositories. */ - Private = 'PRIVATE', + | 'PRIVATE' /** Members will be able to create only public repositories. */ - Public = 'PUBLIC' -} + | 'PUBLIC'; /** The possible values for the members can make purchases setting. */ -export enum EnterpriseMembersCanMakePurchasesSettingValue { +export type EnterpriseMembersCanMakePurchasesSettingValue = /** The setting is disabled for organizations in the enterprise. */ - Disabled = 'DISABLED', + | 'DISABLED' /** The setting is enabled for organizations in the enterprise. */ - Enabled = 'ENABLED' -} + | 'ENABLED'; /** The possible values we have for filtering Platform::Objects::User#enterprises. */ -export enum EnterpriseMembershipType { +export type EnterpriseMembershipType = /** Returns all enterprises in which the user is an admin. */ - Admin = 'ADMIN', + | 'ADMIN' /** Returns all enterprises in which the user is a member, admin, or billing manager. */ - All = 'ALL', + | 'ALL' /** Returns all enterprises in which the user is a billing manager. */ - BillingManager = 'BILLING_MANAGER', + | 'BILLING_MANAGER' /** Returns all enterprises in which the user is a member of an org that is owned by the enterprise. */ - OrgMembership = 'ORG_MEMBERSHIP' -} + | 'ORG_MEMBERSHIP'; /** Ordering options for enterprises. */ export type EnterpriseOrder = { @@ -7450,10 +7410,9 @@ export type EnterpriseOrder = { }; /** Properties by which enterprise connections can be ordered. */ -export enum EnterpriseOrderField { +export type EnterpriseOrderField = /** Order enterprises by name */ - Name = 'NAME' -} + | 'NAME'; /** The connection type for Organization. */ export type EnterpriseOrganizationMembershipConnection = { @@ -8087,14 +8046,13 @@ export type EnterpriseServerInstallationOrder = { }; /** Properties by which Enterprise Server installation connections can be ordered. */ -export enum EnterpriseServerInstallationOrderField { +export type EnterpriseServerInstallationOrderField = /** Order Enterprise Server installations by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order Enterprise Server installations by customer name */ - CustomerName = 'CUSTOMER_NAME', + | 'CUSTOMER_NAME' /** Order Enterprise Server installations by host name */ - HostName = 'HOST_NAME' -} + | 'HOST_NAME'; /** A user account on an Enterprise Server installation. */ export type EnterpriseServerUserAccount = Node & { @@ -8201,10 +8159,9 @@ export type EnterpriseServerUserAccountEmailOrder = { }; /** Properties by which Enterprise Server user account email connections can be ordered. */ -export enum EnterpriseServerUserAccountEmailOrderField { +export type EnterpriseServerUserAccountEmailOrderField = /** Order emails by email */ - Email = 'EMAIL' -} + | 'EMAIL'; /** Ordering options for Enterprise Server user account connections. */ export type EnterpriseServerUserAccountOrder = { @@ -8215,12 +8172,11 @@ export type EnterpriseServerUserAccountOrder = { }; /** Properties by which Enterprise Server user account connections can be ordered. */ -export enum EnterpriseServerUserAccountOrderField { +export type EnterpriseServerUserAccountOrderField = /** Order user accounts by login */ - Login = 'LOGIN', + | 'LOGIN' /** Order user accounts by creation time on the Enterprise Server installation */ - RemoteCreatedAt = 'REMOTE_CREATED_AT' -} + | 'REMOTE_CREATED_AT'; /** A user accounts upload from an Enterprise Server installation. */ export type EnterpriseServerUserAccountsUpload = Node & { @@ -8272,20 +8228,18 @@ export type EnterpriseServerUserAccountsUploadOrder = { }; /** Properties by which Enterprise Server user accounts upload connections can be ordered. */ -export enum EnterpriseServerUserAccountsUploadOrderField { +export type EnterpriseServerUserAccountsUploadOrderField = /** Order user accounts uploads by creation time */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** Synchronization state of the Enterprise Server user accounts upload */ -export enum EnterpriseServerUserAccountsUploadSyncState { +export type EnterpriseServerUserAccountsUploadSyncState = /** The synchronization of the upload failed. */ - Failure = 'FAILURE', + | 'FAILURE' /** The synchronization of the upload is pending. */ - Pending = 'PENDING', + | 'PENDING' /** The synchronization of the upload succeeded. */ - Success = 'SUCCESS' -} + | 'SUCCESS'; /** An account for a user who is an admin of an enterprise or a member of an enterprise through one or more organizations. */ export type EnterpriseUserAccount = Actor & Node & { @@ -8347,22 +8301,20 @@ export type EnterpriseUserAccountOrganizationsArgs = { }; /** The possible roles for enterprise membership. */ -export enum EnterpriseUserAccountMembershipRole { +export type EnterpriseUserAccountMembershipRole = /** The user is a member of an organization in the enterprise. */ - Member = 'MEMBER', + | 'MEMBER' /** The user is an owner of an organization in the enterprise. */ - Owner = 'OWNER', + | 'OWNER' /** The user is not an owner of the enterprise, and not a member or owner of any organizations in the enterprise; only for EMU-enabled enterprises. */ - Unaffiliated = 'UNAFFILIATED' -} + | 'UNAFFILIATED'; /** The possible GitHub Enterprise deployments where this user can exist. */ -export enum EnterpriseUserDeployment { +export type EnterpriseUserDeployment = /** The user is part of a GitHub Enterprise Cloud deployment. */ - Cloud = 'CLOUD', + | 'CLOUD' /** The user is part of a GitHub Enterprise Server deployment. */ - Server = 'SERVER' -} + | 'SERVER'; /** An environment. */ export type Environment = Node & { @@ -8415,20 +8367,18 @@ export type EnvironmentEdge = { }; /** Properties by which environments connections can be ordered */ -export enum EnvironmentOrderField { +export type EnvironmentOrderField = /** Order environments by name. */ - Name = 'NAME' -} + | 'NAME'; /** Properties by which environments connections can be ordered */ -export enum EnvironmentPinnedFilterField { +export type EnvironmentPinnedFilterField = /** All environments will be returned. */ - All = 'ALL', + | 'ALL' /** Environments exclude pinned will be returned */ - None = 'NONE', + | 'NONE' /** Only pinned environment will be returned */ - Only = 'ONLY' -} + | 'ONLY'; /** Ordering options for environments */ export type Environments = { @@ -8686,14 +8636,13 @@ export type FilePathRestrictionParametersInput = { }; /** The possible viewed states of a file . */ -export enum FileViewedState { +export type FileViewedState = /** The file has new changes since last viewed. */ - Dismissed = 'DISMISSED', + | 'DISMISSED' /** The file has not been marked as viewed. */ - Unviewed = 'UNVIEWED', + | 'UNVIEWED' /** The file has been marked as viewed. */ - Viewed = 'VIEWED' -} + | 'VIEWED'; /** Autogenerated input type of FollowOrganization */ export type FollowOrganizationInput = { @@ -8765,34 +8714,33 @@ export type FundingLink = { }; /** The possible funding platforms for repository funding links. */ -export enum FundingPlatform { +export type FundingPlatform = /** Buy Me a Coffee funding platform. */ - BuyMeACoffee = 'BUY_ME_A_COFFEE', + | 'BUY_ME_A_COFFEE' /** Community Bridge funding platform. */ - CommunityBridge = 'COMMUNITY_BRIDGE', + | 'COMMUNITY_BRIDGE' /** Custom funding platform. */ - Custom = 'CUSTOM', + | 'CUSTOM' /** GitHub funding platform. */ - Github = 'GITHUB', + | 'GITHUB' /** IssueHunt funding platform. */ - Issuehunt = 'ISSUEHUNT', + | 'ISSUEHUNT' /** Ko-fi funding platform. */ - KoFi = 'KO_FI', + | 'KO_FI' /** LFX Crowdfunding funding platform. */ - LfxCrowdfunding = 'LFX_CROWDFUNDING', + | 'LFX_CROWDFUNDING' /** Liberapay funding platform. */ - Liberapay = 'LIBERAPAY', + | 'LIBERAPAY' /** Open Collective funding platform. */ - OpenCollective = 'OPEN_COLLECTIVE', + | 'OPEN_COLLECTIVE' /** Patreon funding platform. */ - Patreon = 'PATREON', + | 'PATREON' /** Polar funding platform. */ - Polar = 'POLAR', + | 'POLAR' /** thanks.dev funding platform. */ - ThanksDev = 'THANKS_DEV', + | 'THANKS_DEV' /** Tidelift funding platform. */ - Tidelift = 'TIDELIFT' -} + | 'TIDELIFT'; /** A generic hovercard context with a message and icon */ export type GenericHovercardContext = HovercardContext & { @@ -9025,24 +8973,22 @@ export type GistOrder = { }; /** Properties by which gist connections can be ordered. */ -export enum GistOrderField { +export type GistOrderField = /** Order gists by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order gists by push time */ - PushedAt = 'PUSHED_AT', + | 'PUSHED_AT' /** Order gists by update time */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** The privacy of a Gist */ -export enum GistPrivacy { +export type GistPrivacy = /** Gists that are public and secret */ - All = 'ALL', + | 'ALL' /** Public */ - Public = 'PUBLIC', + | 'PUBLIC' /** Secret */ - Secret = 'SECRET' -} + | 'SECRET'; /** Represents an actor in a Git commit (ie. an author or committer). */ export type GitActor = { @@ -9143,42 +9089,41 @@ export type GitSignature = { }; /** The state of a Git signature. */ -export enum GitSignatureState { +export type GitSignatureState = /** The signing certificate or its chain could not be verified */ - BadCert = 'BAD_CERT', + | 'BAD_CERT' /** Invalid email used for signing */ - BadEmail = 'BAD_EMAIL', + | 'BAD_EMAIL' /** Signing key expired */ - ExpiredKey = 'EXPIRED_KEY', + | 'EXPIRED_KEY' /** Internal error - the GPG verification service misbehaved */ - GpgverifyError = 'GPGVERIFY_ERROR', + | 'GPGVERIFY_ERROR' /** Internal error - the GPG verification service is unavailable at the moment */ - GpgverifyUnavailable = 'GPGVERIFY_UNAVAILABLE', + | 'GPGVERIFY_UNAVAILABLE' /** Invalid signature */ - Invalid = 'INVALID', + | 'INVALID' /** Malformed signature */ - MalformedSig = 'MALFORMED_SIG', + | 'MALFORMED_SIG' /** The usage flags for the key that signed this don't allow signing */ - NotSigningKey = 'NOT_SIGNING_KEY', + | 'NOT_SIGNING_KEY' /** Email used for signing not known to GitHub */ - NoUser = 'NO_USER', + | 'NO_USER' /** Valid signature, though certificate revocation check failed */ - OcspError = 'OCSP_ERROR', + | 'OCSP_ERROR' /** Valid signature, pending certificate revocation checking */ - OcspPending = 'OCSP_PENDING', + | 'OCSP_PENDING' /** One or more certificates in chain has been revoked */ - OcspRevoked = 'OCSP_REVOKED', + | 'OCSP_REVOKED' /** Key used for signing not known to GitHub */ - UnknownKey = 'UNKNOWN_KEY', + | 'UNKNOWN_KEY' /** Unknown signature type */ - UnknownSigType = 'UNKNOWN_SIG_TYPE', + | 'UNKNOWN_SIG_TYPE' /** Unsigned */ - Unsigned = 'UNSIGNED', + | 'UNSIGNED' /** Email used for signing unverified on GitHub */ - UnverifiedEmail = 'UNVERIFIED_EMAIL', + | 'UNVERIFIED_EMAIL' /** Valid signature and verified by GitHub */ - Valid = 'VALID' -} + | 'VALID'; /** Represents a GPG signature on a Commit or Tag. */ export type GpgSignature = GitSignature & { @@ -9317,14 +9262,13 @@ export type HovercardContext = { }; /** The possible states in which authentication can be configured with an identity provider. */ -export enum IdentityProviderConfigurationState { +export type IdentityProviderConfigurationState = /** Authentication with an identity provider is configured but not enforced. */ - Configured = 'CONFIGURED', + | 'CONFIGURED' /** Authentication with an identity provider is configured and enforced. */ - Enforced = 'ENFORCED', + | 'ENFORCED' /** Authentication with an identity provider is not configured. */ - Unconfigured = 'UNCONFIGURED' -} + | 'UNCONFIGURED'; /** Autogenerated input type of ImportProject */ export type ImportProjectInput = { @@ -9396,12 +9340,11 @@ export type InviteEnterpriseMemberPayload = { }; /** The possible values for the IP allow list enabled setting. */ -export enum IpAllowListEnabledSettingValue { +export type IpAllowListEnabledSettingValue = /** The setting is disabled for the owner. */ - Disabled = 'DISABLED', + | 'DISABLED' /** The setting is enabled for the owner. */ - Enabled = 'ENABLED' -} + | 'ENABLED'; /** An IP address or range of addresses that is allowed to access an owner's resources. */ export type IpAllowListEntry = Node & { @@ -9453,20 +9396,18 @@ export type IpAllowListEntryOrder = { }; /** Properties by which IP allow list entry connections can be ordered. */ -export enum IpAllowListEntryOrderField { +export type IpAllowListEntryOrderField = /** Order IP allow list entries by the allow list value. */ - AllowListValue = 'ALLOW_LIST_VALUE', + | 'ALLOW_LIST_VALUE' /** Order IP allow list entries by creation time. */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** The possible values for the IP allow list configuration for installed GitHub Apps setting. */ -export enum IpAllowListForInstalledAppsEnabledSettingValue { +export type IpAllowListForInstalledAppsEnabledSettingValue = /** The setting is disabled for the owner. */ - Disabled = 'DISABLED', + | 'DISABLED' /** The setting is enabled for the owner. */ - Enabled = 'ENABLED' -} + | 'ENABLED'; /** Types that can own an IP allow list. */ export type IpAllowListOwner = App | Enterprise | Organization; @@ -9854,14 +9795,13 @@ export type IssueUserContentEditsArgs = { }; /** The possible state reasons of a closed issue. */ -export enum IssueClosedStateReason { +export type IssueClosedStateReason = /** An issue that has been closed as completed */ - Completed = 'COMPLETED', + | 'COMPLETED' /** An issue that has been closed as a duplicate */ - Duplicate = 'DUPLICATE', + | 'DUPLICATE' /** An issue that has been closed as not planned */ - NotPlanned = 'NOT_PLANNED' -} + | 'NOT_PLANNED'; /** Represents a comment on an Issue. */ export type IssueComment = Comment & Deletable & Minimizable & Node & Reactable & RepositoryNode & Updatable & UpdatableComment & { @@ -9987,10 +9927,9 @@ export type IssueCommentOrder = { }; /** Properties by which issue comment connections can be ordered. */ -export enum IssueCommentOrderField { +export type IssueCommentOrderField = /** Order issue comments by update time */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** The connection type for Issue. */ export type IssueConnection = { @@ -10046,12 +9985,11 @@ export type IssueDependencyOrder = { }; /** Properties by which issue dependencies can be ordered. */ -export enum IssueDependencyOrderField { +export type IssueDependencyOrderField = /** Order issue dependencies by the creation time of the dependent issue */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order issue dependencies by time of when the dependency relationship was added */ - DependencyAddedAt = 'DEPENDENCY_ADDED_AT' -} + | 'DEPENDENCY_ADDED_AT'; /** An edge in a connection. */ export type IssueEdge = { @@ -10098,34 +10036,31 @@ export type IssueOrder = { }; /** Properties by which issue connections can be ordered. */ -export enum IssueOrderField { +export type IssueOrderField = /** Order issues by comment count */ - Comments = 'COMMENTS', + | 'COMMENTS' /** Order issues by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order issues by update time */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** The possible states of an issue. */ -export enum IssueState { +export type IssueState = /** An issue that has been closed */ - Closed = 'CLOSED', + | 'CLOSED' /** An issue that is still open */ - Open = 'OPEN' -} + | 'OPEN'; /** The possible state reasons of an issue. */ -export enum IssueStateReason { +export type IssueStateReason = /** An issue that has been closed as completed */ - Completed = 'COMPLETED', + | 'COMPLETED' /** An issue that has been closed as a duplicate. */ - Duplicate = 'DUPLICATE', + | 'DUPLICATE' /** An issue that has been closed as not planned */ - NotPlanned = 'NOT_PLANNED', + | 'NOT_PLANNED' /** An issue that has been reopened */ - Reopened = 'REOPENED' -} + | 'REOPENED'; /** A repository issue template. */ export type IssueTemplate = { @@ -10224,106 +10159,105 @@ export type IssueTimelineItemsEdge = { }; /** The possible item types found in a timeline. */ -export enum IssueTimelineItemsItemType { +export type IssueTimelineItemsItemType = /** Represents a 'added_to_project' event on a given issue or pull request. */ - AddedToProjectEvent = 'ADDED_TO_PROJECT_EVENT', + | 'ADDED_TO_PROJECT_EVENT' /** Represents a 'added_to_project_v2' event on a given issue or pull request. */ - AddedToProjectV2Event = 'ADDED_TO_PROJECT_V2_EVENT', + | 'ADDED_TO_PROJECT_V2_EVENT' /** Represents an 'assigned' event on any assignable object. */ - AssignedEvent = 'ASSIGNED_EVENT', + | 'ASSIGNED_EVENT' /** Represents a 'blocked_by_added' event on a given issue. */ - BlockedByAddedEvent = 'BLOCKED_BY_ADDED_EVENT', + | 'BLOCKED_BY_ADDED_EVENT' /** Represents a 'blocked_by_removed' event on a given issue. */ - BlockedByRemovedEvent = 'BLOCKED_BY_REMOVED_EVENT', + | 'BLOCKED_BY_REMOVED_EVENT' /** Represents a 'blocking_added' event on a given issue. */ - BlockingAddedEvent = 'BLOCKING_ADDED_EVENT', + | 'BLOCKING_ADDED_EVENT' /** Represents a 'blocking_removed' event on a given issue. */ - BlockingRemovedEvent = 'BLOCKING_REMOVED_EVENT', + | 'BLOCKING_REMOVED_EVENT' /** Represents a 'closed' event on any `Closable`. */ - ClosedEvent = 'CLOSED_EVENT', + | 'CLOSED_EVENT' /** Represents a 'comment_deleted' event on a given issue or pull request. */ - CommentDeletedEvent = 'COMMENT_DELETED_EVENT', + | 'COMMENT_DELETED_EVENT' /** Represents a 'connected' event on a given issue or pull request. */ - ConnectedEvent = 'CONNECTED_EVENT', + | 'CONNECTED_EVENT' /** Represents a 'converted_from_draft' event on a given issue or pull request. */ - ConvertedFromDraftEvent = 'CONVERTED_FROM_DRAFT_EVENT', + | 'CONVERTED_FROM_DRAFT_EVENT' /** Represents a 'converted_note_to_issue' event on a given issue or pull request. */ - ConvertedNoteToIssueEvent = 'CONVERTED_NOTE_TO_ISSUE_EVENT', + | 'CONVERTED_NOTE_TO_ISSUE_EVENT' /** Represents a 'converted_to_discussion' event on a given issue. */ - ConvertedToDiscussionEvent = 'CONVERTED_TO_DISCUSSION_EVENT', + | 'CONVERTED_TO_DISCUSSION_EVENT' /** Represents a mention made by one issue or pull request to another. */ - CrossReferencedEvent = 'CROSS_REFERENCED_EVENT', + | 'CROSS_REFERENCED_EVENT' /** Represents a 'demilestoned' event on a given issue or pull request. */ - DemilestonedEvent = 'DEMILESTONED_EVENT', + | 'DEMILESTONED_EVENT' /** Represents a 'disconnected' event on a given issue or pull request. */ - DisconnectedEvent = 'DISCONNECTED_EVENT', + | 'DISCONNECTED_EVENT' /** Represents a comment on an Issue. */ - IssueComment = 'ISSUE_COMMENT', + | 'ISSUE_COMMENT' /** Represents a 'issue_field_added' event on a given issue. */ - IssueFieldAddedEvent = 'ISSUE_FIELD_ADDED_EVENT', + | 'ISSUE_FIELD_ADDED_EVENT' /** Represents a 'issue_field_changed' event on a given issue. */ - IssueFieldChangedEvent = 'ISSUE_FIELD_CHANGED_EVENT', + | 'ISSUE_FIELD_CHANGED_EVENT' /** Represents a 'issue_field_removed' event on a given issue. */ - IssueFieldRemovedEvent = 'ISSUE_FIELD_REMOVED_EVENT', + | 'ISSUE_FIELD_REMOVED_EVENT' /** Represents a 'issue_type_added' event on a given issue. */ - IssueTypeAddedEvent = 'ISSUE_TYPE_ADDED_EVENT', + | 'ISSUE_TYPE_ADDED_EVENT' /** Represents a 'issue_type_changed' event on a given issue. */ - IssueTypeChangedEvent = 'ISSUE_TYPE_CHANGED_EVENT', + | 'ISSUE_TYPE_CHANGED_EVENT' /** Represents a 'issue_type_removed' event on a given issue. */ - IssueTypeRemovedEvent = 'ISSUE_TYPE_REMOVED_EVENT', + | 'ISSUE_TYPE_REMOVED_EVENT' /** Represents a 'labeled' event on a given issue or pull request. */ - LabeledEvent = 'LABELED_EVENT', + | 'LABELED_EVENT' /** Represents a 'locked' event on a given issue or pull request. */ - LockedEvent = 'LOCKED_EVENT', + | 'LOCKED_EVENT' /** Represents a 'marked_as_duplicate' event on a given issue or pull request. */ - MarkedAsDuplicateEvent = 'MARKED_AS_DUPLICATE_EVENT', + | 'MARKED_AS_DUPLICATE_EVENT' /** Represents a 'mentioned' event on a given issue or pull request. */ - MentionedEvent = 'MENTIONED_EVENT', + | 'MENTIONED_EVENT' /** Represents a 'milestoned' event on a given issue or pull request. */ - MilestonedEvent = 'MILESTONED_EVENT', + | 'MILESTONED_EVENT' /** Represents a 'moved_columns_in_project' event on a given issue or pull request. */ - MovedColumnsInProjectEvent = 'MOVED_COLUMNS_IN_PROJECT_EVENT', + | 'MOVED_COLUMNS_IN_PROJECT_EVENT' /** Represents a 'parent_issue_added' event on a given issue. */ - ParentIssueAddedEvent = 'PARENT_ISSUE_ADDED_EVENT', + | 'PARENT_ISSUE_ADDED_EVENT' /** Represents a 'parent_issue_removed' event on a given issue. */ - ParentIssueRemovedEvent = 'PARENT_ISSUE_REMOVED_EVENT', + | 'PARENT_ISSUE_REMOVED_EVENT' /** Represents a 'pinned' event on a given issue or pull request. */ - PinnedEvent = 'PINNED_EVENT', + | 'PINNED_EVENT' /** Represents a 'project_v2_item_status_changed' event on a given issue or pull request. */ - ProjectV2ItemStatusChangedEvent = 'PROJECT_V2_ITEM_STATUS_CHANGED_EVENT', + | 'PROJECT_V2_ITEM_STATUS_CHANGED_EVENT' /** Represents a 'referenced' event on a given `ReferencedSubject`. */ - ReferencedEvent = 'REFERENCED_EVENT', + | 'REFERENCED_EVENT' /** Represents a 'removed_from_project' event on a given issue or pull request. */ - RemovedFromProjectEvent = 'REMOVED_FROM_PROJECT_EVENT', + | 'REMOVED_FROM_PROJECT_EVENT' /** Represents a 'removed_from_project_v2' event on a given issue or pull request. */ - RemovedFromProjectV2Event = 'REMOVED_FROM_PROJECT_V2_EVENT', + | 'REMOVED_FROM_PROJECT_V2_EVENT' /** Represents a 'renamed' event on a given issue or pull request */ - RenamedTitleEvent = 'RENAMED_TITLE_EVENT', + | 'RENAMED_TITLE_EVENT' /** Represents a 'reopened' event on any `Closable`. */ - ReopenedEvent = 'REOPENED_EVENT', + | 'REOPENED_EVENT' /** Represents a 'subscribed' event on a given `Subscribable`. */ - SubscribedEvent = 'SUBSCRIBED_EVENT', + | 'SUBSCRIBED_EVENT' /** Represents a 'sub_issue_added' event on a given issue. */ - SubIssueAddedEvent = 'SUB_ISSUE_ADDED_EVENT', + | 'SUB_ISSUE_ADDED_EVENT' /** Represents a 'sub_issue_removed' event on a given issue. */ - SubIssueRemovedEvent = 'SUB_ISSUE_REMOVED_EVENT', + | 'SUB_ISSUE_REMOVED_EVENT' /** Represents a 'transferred' event on a given issue or pull request. */ - TransferredEvent = 'TRANSFERRED_EVENT', + | 'TRANSFERRED_EVENT' /** Represents an 'unassigned' event on any assignable object. */ - UnassignedEvent = 'UNASSIGNED_EVENT', + | 'UNASSIGNED_EVENT' /** Represents an 'unlabeled' event on a given issue or pull request. */ - UnlabeledEvent = 'UNLABELED_EVENT', + | 'UNLABELED_EVENT' /** Represents an 'unlocked' event on a given issue or pull request. */ - UnlockedEvent = 'UNLOCKED_EVENT', + | 'UNLOCKED_EVENT' /** Represents an 'unmarked_as_duplicate' event on a given issue or pull request. */ - UnmarkedAsDuplicateEvent = 'UNMARKED_AS_DUPLICATE_EVENT', + | 'UNMARKED_AS_DUPLICATE_EVENT' /** Represents an 'unpinned' event on a given issue or pull request. */ - UnpinnedEvent = 'UNPINNED_EVENT', + | 'UNPINNED_EVENT' /** Represents an 'unsubscribed' event on a given `Subscribable`. */ - UnsubscribedEvent = 'UNSUBSCRIBED_EVENT', + | 'UNSUBSCRIBED_EVENT' /** Represents a 'user_blocked' event on a given user. */ - UserBlockedEvent = 'USER_BLOCKED_EVENT' -} + | 'USER_BLOCKED_EVENT'; /** Represents the type of Issue. */ export type IssueType = Node & { @@ -10390,24 +10324,23 @@ export type IssueTypeChangedEvent = Node & { }; /** The possible color for an issue type */ -export enum IssueTypeColor { +export type IssueTypeColor = /** blue */ - Blue = 'BLUE', + | 'BLUE' /** gray */ - Gray = 'GRAY', + | 'GRAY' /** green */ - Green = 'GREEN', + | 'GREEN' /** orange */ - Orange = 'ORANGE', + | 'ORANGE' /** pink */ - Pink = 'PINK', + | 'PINK' /** purple */ - Purple = 'PURPLE', + | 'PURPLE' /** red */ - Red = 'RED', + | 'RED' /** yellow */ - Yellow = 'YELLOW' -} + | 'YELLOW'; /** The connection type for IssueType. */ export type IssueTypeConnection = { @@ -10440,12 +10373,11 @@ export type IssueTypeOrder = { }; /** Properties by which issue type connections can be ordered. */ -export enum IssueTypeOrderField { +export type IssueTypeOrderField = /** Order issue types by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order issue types by name */ - Name = 'NAME' -} + | 'NAME'; /** Represents a 'issue_type_removed' event on a given issue. */ export type IssueTypeRemovedEvent = Node & { @@ -10570,14 +10502,13 @@ export type LabelOrder = { }; /** Properties by which label connections can be ordered. */ -export enum LabelOrderField { +export type LabelOrderField = /** Order labels by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order labels by issue count */ - IssueCount = 'ISSUE_COUNT', + | 'ISSUE_COUNT' /** Order labels by name */ - Name = 'NAME' -} + | 'NAME'; /** An object that can have labels assigned to it. */ export type Labelable = { @@ -10656,10 +10587,9 @@ export type LanguageOrder = { }; /** Properties by which language connections can be ordered. */ -export enum LanguageOrderField { +export type LanguageOrderField = /** Order languages by the size of all files containing the language */ - Size = 'SIZE' -} + | 'SIZE'; /** A repository's open source license */ export type License = Node & { @@ -10819,16 +10749,15 @@ export type LockLockablePayload = { }; /** The possible reasons that an issue or pull request was locked. */ -export enum LockReason { +export type LockReason = /** The issue or pull request was locked because the conversation was off-topic. */ - OffTopic = 'OFF_TOPIC', + | 'OFF_TOPIC' /** The issue or pull request was locked because the conversation was resolved. */ - Resolved = 'RESOLVED', + | 'RESOLVED' /** The issue or pull request was locked because the conversation was spam. */ - Spam = 'SPAM', + | 'SPAM' /** The issue or pull request was locked because the conversation was too heated. */ - TooHeated = 'TOO_HEATED' -} + | 'TOO_HEATED'; /** An object that can be locked. */ export type Lockable = { @@ -10917,12 +10846,11 @@ export type MannequinOrder = { }; /** Properties by which mannequins can be ordered. */ -export enum MannequinOrderField { +export type MannequinOrderField = /** Order mannequins why when they were created. */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order mannequins alphabetically by their source login. */ - Login = 'LOGIN' -} + | 'LOGIN'; /** Autogenerated input type of MarkDiscussionCommentAsAnswer */ export type MarkDiscussionCommentAsAnswerInput = { @@ -11593,22 +11521,20 @@ export type MergeBranchPayload = { }; /** The possible default commit messages for merges. */ -export enum MergeCommitMessage { +export type MergeCommitMessage = /** Default to a blank commit message. */ - Blank = 'BLANK', + | 'BLANK' /** Default to the pull request's body. */ - PrBody = 'PR_BODY', + | 'PR_BODY' /** Default to the pull request's title. */ - PrTitle = 'PR_TITLE' -} + | 'PR_TITLE'; /** The possible default commit titles for merges. */ -export enum MergeCommitTitle { +export type MergeCommitTitle = /** Default to the classic title for a merge message (e.g., Merge pull request #123 from branch-name). */ - MergeMessage = 'MERGE_MESSAGE', + | 'MERGE_MESSAGE' /** Default to the pull request's title. */ - PrTitle = 'PR_TITLE' -} + | 'PR_TITLE'; /** Autogenerated input type of MergePullRequest */ export type MergePullRequestInput = { @@ -11738,44 +11664,40 @@ export type MergeQueueEntryEdge = { }; /** The possible states for a merge queue entry. */ -export enum MergeQueueEntryState { +export type MergeQueueEntryState = /** The entry is currently waiting for checks to pass. */ - AwaitingChecks = 'AWAITING_CHECKS', + | 'AWAITING_CHECKS' /** The entry is currently locked. */ - Locked = 'LOCKED', + | 'LOCKED' /** The entry is currently mergeable. */ - Mergeable = 'MERGEABLE', + | 'MERGEABLE' /** The entry is currently queued. */ - Queued = 'QUEUED', + | 'QUEUED' /** The entry is currently unmergeable. */ - Unmergeable = 'UNMERGEABLE' -} + | 'UNMERGEABLE'; /** When set to ALLGREEN, the merge commit created by merge queue for each PR in the group must pass all required checks to merge. When set to HEADGREEN, only the commit at the head of the merge group, i.e. the commit containing changes from all of the PRs in the group, must pass its required checks to merge. */ -export enum MergeQueueGroupingStrategy { +export type MergeQueueGroupingStrategy = /** The merge commit created by merge queue for each PR in the group must pass all required checks to merge */ - Allgreen = 'ALLGREEN', + | 'ALLGREEN' /** Only the commit at the head of the merge group must pass its required checks to merge. */ - Headgreen = 'HEADGREEN' -} + | 'HEADGREEN'; /** Method to use when merging changes from queued pull requests. */ -export enum MergeQueueMergeMethod { +export type MergeQueueMergeMethod = /** Merge commit */ - Merge = 'MERGE', + | 'MERGE' /** Rebase and merge */ - Rebase = 'REBASE', + | 'REBASE' /** Squash and merge */ - Squash = 'SQUASH' -} + | 'SQUASH'; /** The possible merging strategies for a merge queue. */ -export enum MergeQueueMergingStrategy { +export type MergeQueueMergingStrategy = /** Entries only allowed to merge if they are passing. */ - Allgreen = 'ALLGREEN', + | 'ALLGREEN' /** Failing Entires are allowed to merge if they are with a passing entry. */ - Headgreen = 'HEADGREEN' -} + | 'HEADGREEN'; /** Merges must be performed via a merge queue. */ export type MergeQueueParameters = { @@ -11815,37 +11737,32 @@ export type MergeQueueParametersInput = { }; /** Detailed status information about a pull request merge. */ -export enum MergeStateStatus { +export type MergeStateStatus = /** The head ref is out of date. */ - Behind = 'BEHIND', + | 'BEHIND' /** The merge is blocked. */ - Blocked = 'BLOCKED', + | 'BLOCKED' /** Mergeable and passing commit status. */ - Clean = 'CLEAN', + | 'CLEAN' /** The merge commit cannot be cleanly created. */ - Dirty = 'DIRTY', - /** - * The merge is blocked due to the pull request being a draft. - * @deprecated DRAFT state will be removed from this enum and `isDraft` should be used instead Use PullRequest.isDraft instead. Removal on 2021-01-01 UTC. - */ - Draft = 'DRAFT', + | 'DIRTY' + /** The merge is blocked due to the pull request being a draft. */ + | 'DRAFT' /** Mergeable with passing commit status and pre-receive hooks. */ - HasHooks = 'HAS_HOOKS', + | 'HAS_HOOKS' /** The state cannot currently be determined. */ - Unknown = 'UNKNOWN', + | 'UNKNOWN' /** Mergeable with non-passing commit status. */ - Unstable = 'UNSTABLE' -} + | 'UNSTABLE'; /** Whether or not a PullRequest can be merged. */ -export enum MergeableState { +export type MergeableState = /** The pull request cannot be merged due to merge conflicts. */ - Conflicting = 'CONFLICTING', + | 'CONFLICTING' /** The pull request can be merged. */ - Mergeable = 'MERGEABLE', + | 'MERGEABLE' /** The mergeability of the pull request is still being calculated. */ - Unknown = 'UNKNOWN' -} + | 'UNKNOWN'; /** Represents a 'merged' event on a given pull request. */ export type MergedEvent = Node & UniformResourceLocatable & { @@ -11910,32 +11827,30 @@ export type MigrationSource = Node & { }; /** Represents the different GitHub Enterprise Importer (GEI) migration sources. */ -export enum MigrationSourceType { +export type MigrationSourceType = /** An Azure DevOps migration source. */ - AzureDevops = 'AZURE_DEVOPS', + | 'AZURE_DEVOPS' /** A Bitbucket Server migration source. */ - BitbucketServer = 'BITBUCKET_SERVER', + | 'BITBUCKET_SERVER' /** A GitHub Migration API source. */ - GithubArchive = 'GITHUB_ARCHIVE' -} + | 'GITHUB_ARCHIVE'; /** The GitHub Enterprise Importer (GEI) migration state. */ -export enum MigrationState { +export type MigrationState = /** The migration has failed. */ - Failed = 'FAILED', + | 'FAILED' /** The migration has invalid credentials. */ - FailedValidation = 'FAILED_VALIDATION', + | 'FAILED_VALIDATION' /** The migration is in progress. */ - InProgress = 'IN_PROGRESS', + | 'IN_PROGRESS' /** The migration has not started. */ - NotStarted = 'NOT_STARTED', + | 'NOT_STARTED' /** The migration needs to have its credentials validated. */ - PendingValidation = 'PENDING_VALIDATION', + | 'PENDING_VALIDATION' /** The migration has been queued. */ - Queued = 'QUEUED', + | 'QUEUED' /** The migration has succeeded. */ - Succeeded = 'SUCCEEDED' -} + | 'SUCCEEDED'; /** Represents a Milestone object on a given repository. */ export type Milestone = Closable & Node & UniformResourceLocatable & { @@ -12047,24 +11962,22 @@ export type MilestoneOrder = { }; /** Properties by which milestone connections can be ordered. */ -export enum MilestoneOrderField { +export type MilestoneOrderField = /** Order milestones by when they were created. */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order milestones by when they are due. */ - DueDate = 'DUE_DATE', + | 'DUE_DATE' /** Order milestones by their number. */ - Number = 'NUMBER', + | 'NUMBER' /** Order milestones by when they were last updated. */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** The possible states of a milestone. */ -export enum MilestoneState { +export type MilestoneState = /** A milestone that has been closed. */ - Closed = 'CLOSED', + | 'CLOSED' /** A milestone that is still open. */ - Open = 'OPEN' -} + | 'OPEN'; /** Represents a 'milestoned' event on a given issue or pull request. */ export type MilestonedEvent = Node & { @@ -14360,12 +14273,11 @@ export type Node = { }; /** The possible values for the notification restriction setting. */ -export enum NotificationRestrictionSettingValue { +export type NotificationRestrictionSettingValue = /** The setting is disabled for the owner. */ - Disabled = 'DISABLED', + | 'DISABLED' /** The setting is enabled for the owner. */ - Enabled = 'ENABLED' -} + | 'ENABLED'; /** An OIDC identity provider configured to provision identities for an enterprise. Visible to enterprise owners or enterprise owners' personal access tokens (classic) with read:enterprise or admin:enterprise scope. */ export type OidcProvider = Node & { @@ -14395,10 +14307,9 @@ export type OidcProviderExternalIdentitiesArgs = { }; /** The OIDC identity provider type */ -export enum OidcProviderType { +export type OidcProviderType = /** Azure Active Directory */ - Aad = 'AAD' -} + | 'AAD'; /** Metadata for an audit entry with action oauth_application.* */ export type OauthApplicationAuditEntryData = { @@ -14529,40 +14440,37 @@ export type OauthApplicationCreateAuditEntry = AuditEntry & Node & OauthApplicat }; /** The state of an OAuth application when it was created. */ -export enum OauthApplicationCreateAuditEntryState { +export type OauthApplicationCreateAuditEntryState = /** The OAuth application was active and allowed to have OAuth Accesses. */ - Active = 'ACTIVE', + | 'ACTIVE' /** The OAuth application was in the process of being deleted. */ - PendingDeletion = 'PENDING_DELETION', + | 'PENDING_DELETION' /** The OAuth application was suspended from generating OAuth Accesses due to abuse or security concerns. */ - Suspended = 'SUSPENDED' -} + | 'SUSPENDED'; /** The corresponding operation type for the action */ -export enum OperationType { +export type OperationType = /** An existing resource was accessed */ - Access = 'ACCESS', + | 'ACCESS' /** A resource performed an authentication event */ - Authentication = 'AUTHENTICATION', + | 'AUTHENTICATION' /** A new resource was created */ - Create = 'CREATE', + | 'CREATE' /** An existing resource was modified */ - Modify = 'MODIFY', + | 'MODIFY' /** An existing resource was removed */ - Remove = 'REMOVE', + | 'REMOVE' /** An existing resource was restored */ - Restore = 'RESTORE', + | 'RESTORE' /** An existing resource was transferred between multiple resources */ - Transfer = 'TRANSFER' -} + | 'TRANSFER'; /** Possible directions in which to order a list of items when provided an `orderBy` argument. */ -export enum OrderDirection { +export type OrderDirection = /** Specifies an ascending order for a given `orderBy` argument. */ - Asc = 'ASC', + | 'ASC' /** Specifies a descending order for a given `orderBy` argument. */ - Desc = 'DESC' -} + | 'DESC'; /** Audit log entry for a org.add_billing_manager */ export type OrgAddBillingManagerAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & { @@ -14759,12 +14667,11 @@ export type OrgAddMemberAuditEntry = AuditEntry & Node & OrganizationAuditEntryD }; /** The permissions available to members on an Organization. */ -export enum OrgAddMemberAuditEntryPermission { +export type OrgAddMemberAuditEntryPermission = /** Can read, clone, push, and add collaborators to repositories. */ - Admin = 'ADMIN', + | 'ADMIN' /** Can read and clone repositories. */ - Read = 'READ' -} + | 'READ'; /** Audit log entry for a org.block_user */ export type OrgBlockUserAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & { @@ -15160,18 +15067,17 @@ export type OrgCreateAuditEntry = AuditEntry & Node & OrganizationAuditEntryData }; /** The billing plans available for organizations. */ -export enum OrgCreateAuditEntryBillingPlan { +export type OrgCreateAuditEntryBillingPlan = /** Team Plan */ - Business = 'BUSINESS', + | 'BUSINESS' /** Enterprise Cloud Plan */ - BusinessPlus = 'BUSINESS_PLUS', + | 'BUSINESS_PLUS' /** Free Plan */ - Free = 'FREE', + | 'FREE' /** Tiered Per Seat Plan */ - TieredPerSeat = 'TIERED_PER_SEAT', + | 'TIERED_PER_SEAT' /** Legacy Unlimited Plan */ - Unlimited = 'UNLIMITED' -} + | 'UNLIMITED'; /** Audit log entry for a org.disable_oauth_app_restrictions event. */ export type OrgDisableOauthAppRestrictionsAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & { @@ -15774,10 +15680,9 @@ export type OrgEnterpriseOwnerOrder = { }; /** Properties by which enterprise owners can be ordered. */ -export enum OrgEnterpriseOwnerOrderField { +export type OrgEnterpriseOwnerOrderField = /** Order enterprise owners by login. */ - Login = 'LOGIN' -} + | 'LOGIN'; /** Audit log entry for a org.invite_member event. */ export type OrgInviteMemberAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & { @@ -16567,14 +16472,13 @@ export type OrgRemoveBillingManagerAuditEntry = AuditEntry & Node & Organization }; /** The reason a billing manager was removed from an Organization. */ -export enum OrgRemoveBillingManagerAuditEntryReason { +export type OrgRemoveBillingManagerAuditEntryReason = /** SAML external identity missing */ - SamlExternalIdentityMissing = 'SAML_EXTERNAL_IDENTITY_MISSING', + | 'SAML_EXTERNAL_IDENTITY_MISSING' /** SAML SSO enforcement requires an external identity */ - SamlSsoEnforcementRequiresExternalIdentity = 'SAML_SSO_ENFORCEMENT_REQUIRES_EXTERNAL_IDENTITY', + | 'SAML_SSO_ENFORCEMENT_REQUIRES_EXTERNAL_IDENTITY' /** The organization required 2FA of its billing managers and this user did not have 2FA enabled. */ - TwoFactorRequirementNonCompliance = 'TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE' -} + | 'TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE'; /** Audit log entry for a org.remove_member event. */ export type OrgRemoveMemberAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & { @@ -16679,34 +16583,32 @@ export type OrgRemoveMemberAuditEntry = AuditEntry & Node & OrganizationAuditEnt }; /** The type of membership a user has with an Organization. */ -export enum OrgRemoveMemberAuditEntryMembershipType { +export type OrgRemoveMemberAuditEntryMembershipType = /** Organization owners have full access and can change several settings, including the names of repositories that belong to the Organization and Owners team membership. In addition, organization owners can delete the organization and all of its repositories. */ - Admin = 'ADMIN', + | 'ADMIN' /** A billing manager is a user who manages the billing settings for the Organization, such as updating payment information. */ - BillingManager = 'BILLING_MANAGER', + | 'BILLING_MANAGER' /** A direct member is a user that is a member of the Organization. */ - DirectMember = 'DIRECT_MEMBER', + | 'DIRECT_MEMBER' /** An outside collaborator is a person who isn't explicitly a member of the Organization, but who has Read, Write, or Admin permissions to one or more repositories in the organization. */ - OutsideCollaborator = 'OUTSIDE_COLLABORATOR', + | 'OUTSIDE_COLLABORATOR' /** A suspended member. */ - Suspended = 'SUSPENDED', + | 'SUSPENDED' /** An unaffiliated collaborator is a person who is not a member of the Organization and does not have access to any repositories in the Organization. */ - Unaffiliated = 'UNAFFILIATED' -} + | 'UNAFFILIATED'; /** The reason a member was removed from an Organization. */ -export enum OrgRemoveMemberAuditEntryReason { +export type OrgRemoveMemberAuditEntryReason = /** SAML external identity missing */ - SamlExternalIdentityMissing = 'SAML_EXTERNAL_IDENTITY_MISSING', + | 'SAML_EXTERNAL_IDENTITY_MISSING' /** SAML SSO enforcement requires an external identity */ - SamlSsoEnforcementRequiresExternalIdentity = 'SAML_SSO_ENFORCEMENT_REQUIRES_EXTERNAL_IDENTITY', + | 'SAML_SSO_ENFORCEMENT_REQUIRES_EXTERNAL_IDENTITY' /** User was removed from organization during account recovery */ - TwoFactorAccountRecovery = 'TWO_FACTOR_ACCOUNT_RECOVERY', + | 'TWO_FACTOR_ACCOUNT_RECOVERY' /** The organization required 2FA of its billing managers and this user did not have 2FA enabled. */ - TwoFactorRequirementNonCompliance = 'TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE', + | 'TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE' /** User account has been deleted */ - UserAccountDeleted = 'USER_ACCOUNT_DELETED' -} + | 'USER_ACCOUNT_DELETED'; /** Audit log entry for a org.remove_outside_collaborator event. */ export type OrgRemoveOutsideCollaboratorAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & { @@ -16811,22 +16713,20 @@ export type OrgRemoveOutsideCollaboratorAuditEntry = AuditEntry & Node & Organiz }; /** The type of membership a user has with an Organization. */ -export enum OrgRemoveOutsideCollaboratorAuditEntryMembershipType { +export type OrgRemoveOutsideCollaboratorAuditEntryMembershipType = /** A billing manager is a user who manages the billing settings for the Organization, such as updating payment information. */ - BillingManager = 'BILLING_MANAGER', + | 'BILLING_MANAGER' /** An outside collaborator is a person who isn't explicitly a member of the Organization, but who has Read, Write, or Admin permissions to one or more repositories in the organization. */ - OutsideCollaborator = 'OUTSIDE_COLLABORATOR', + | 'OUTSIDE_COLLABORATOR' /** An unaffiliated collaborator is a person who is not a member of the Organization and does not have access to any repositories in the organization. */ - Unaffiliated = 'UNAFFILIATED' -} + | 'UNAFFILIATED'; /** The reason an outside collaborator was removed from an Organization. */ -export enum OrgRemoveOutsideCollaboratorAuditEntryReason { +export type OrgRemoveOutsideCollaboratorAuditEntryReason = /** SAML external identity missing */ - SamlExternalIdentityMissing = 'SAML_EXTERNAL_IDENTITY_MISSING', + | 'SAML_EXTERNAL_IDENTITY_MISSING' /** The organization required 2FA of its billing managers and this user did not have 2FA enabled. */ - TwoFactorRequirementNonCompliance = 'TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE' -} + | 'TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE'; /** Audit log entry for a org.restore_member event. */ export type OrgRestoreMemberAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & { @@ -17224,16 +17124,15 @@ export type OrgUpdateDefaultRepositoryPermissionAuditEntry = AuditEntry & Node & }; /** The default permission a repository can have in an Organization. */ -export enum OrgUpdateDefaultRepositoryPermissionAuditEntryPermission { +export type OrgUpdateDefaultRepositoryPermissionAuditEntryPermission = /** Can read, clone, push, and add collaborators to repositories. */ - Admin = 'ADMIN', + | 'ADMIN' /** No default permission value. */ - None = 'NONE', + | 'NONE' /** Can read and clone repositories. */ - Read = 'READ', + | 'READ' /** Can read, clone and push to repositories. */ - Write = 'WRITE' -} + | 'WRITE'; /** Audit log entry for a org.update_member event. */ export type OrgUpdateMemberAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & { @@ -17338,12 +17237,11 @@ export type OrgUpdateMemberAuditEntry = AuditEntry & Node & OrganizationAuditEnt }; /** The permissions available to members on an Organization. */ -export enum OrgUpdateMemberAuditEntryPermission { +export type OrgUpdateMemberAuditEntryPermission = /** Can read, clone, push, and add collaborators to repositories. */ - Admin = 'ADMIN', + | 'ADMIN' /** Can read and clone repositories. */ - Read = 'READ' -} + | 'READ'; /** Audit log entry for a org.update_member_repository_creation_permission event. */ export type OrgUpdateMemberRepositoryCreationPermissionAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & { @@ -17448,24 +17346,23 @@ export type OrgUpdateMemberRepositoryCreationPermissionAuditEntry = AuditEntry & }; /** The permissions available for repository creation on an Organization. */ -export enum OrgUpdateMemberRepositoryCreationPermissionAuditEntryVisibility { +export type OrgUpdateMemberRepositoryCreationPermissionAuditEntryVisibility = /** All organization members are restricted from creating any repositories. */ - All = 'ALL', + | 'ALL' /** All organization members are restricted from creating internal repositories. */ - Internal = 'INTERNAL', + | 'INTERNAL' /** All organization members are allowed to create any repositories. */ - None = 'NONE', + | 'NONE' /** All organization members are restricted from creating private repositories. */ - Private = 'PRIVATE', + | 'PRIVATE' /** All organization members are restricted from creating private or internal repositories. */ - PrivateInternal = 'PRIVATE_INTERNAL', + | 'PRIVATE_INTERNAL' /** All organization members are restricted from creating public repositories. */ - Public = 'PUBLIC', + | 'PUBLIC' /** All organization members are restricted from creating public or internal repositories. */ - PublicInternal = 'PUBLIC_INTERNAL', + | 'PUBLIC_INTERNAL' /** All organization members are restricted from creating public or private repositories. */ - PublicPrivate = 'PUBLIC_PRIVATE' -} + | 'PUBLIC_PRIVATE'; /** Audit log entry for a org.update_member_repository_invitation_permission event. */ export type OrgUpdateMemberRepositoryInvitationPermissionAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & { @@ -18346,34 +18243,31 @@ export type OrganizationInvitationEdge = { }; /** The possible organization invitation roles. */ -export enum OrganizationInvitationRole { +export type OrganizationInvitationRole = /** The user is invited to be an admin of the organization. */ - Admin = 'ADMIN', + | 'ADMIN' /** The user is invited to be a billing manager of the organization. */ - BillingManager = 'BILLING_MANAGER', + | 'BILLING_MANAGER' /** The user is invited to be a direct member of the organization. */ - DirectMember = 'DIRECT_MEMBER', + | 'DIRECT_MEMBER' /** The user's previous role will be reinstated. */ - Reinstate = 'REINSTATE' -} + | 'REINSTATE'; /** The possible organization invitation sources. */ -export enum OrganizationInvitationSource { +export type OrganizationInvitationSource = /** The invitation was created from the web interface or from API */ - Member = 'MEMBER', + | 'MEMBER' /** The invitation was created from SCIM */ - Scim = 'SCIM', + | 'SCIM' /** The invitation was sent before this feature was added */ - Unknown = 'UNKNOWN' -} + | 'UNKNOWN'; /** The possible organization invitation types. */ -export enum OrganizationInvitationType { +export type OrganizationInvitationType = /** The invitation was to an email address. */ - Email = 'EMAIL', + | 'EMAIL' /** The invitation was to an existing user. */ - User = 'USER' -} + | 'USER'; /** A list of users who belong to the organization. */ export type OrganizationMemberConnection = { @@ -18402,24 +18296,22 @@ export type OrganizationMemberEdge = { }; /** The possible roles within an organization for its members. */ -export enum OrganizationMemberRole { +export type OrganizationMemberRole = /** The user is an administrator of the organization. */ - Admin = 'ADMIN', + | 'ADMIN' /** The user is a member of the organization. */ - Member = 'MEMBER' -} + | 'MEMBER'; /** The possible values for the members can create repositories setting on an organization. */ -export enum OrganizationMembersCanCreateRepositoriesSettingValue { +export type OrganizationMembersCanCreateRepositoriesSettingValue = /** Members will be able to create public and private repositories. */ - All = 'ALL', + | 'ALL' /** Members will not be able to create public or private repositories. */ - Disabled = 'DISABLED', + | 'DISABLED' /** Members will be able to create only internal repositories. */ - Internal = 'INTERNAL', + | 'INTERNAL' /** Members will be able to create only private repositories. */ - Private = 'PRIVATE' -} + | 'PRIVATE'; /** A GitHub Enterprise Importer (GEI) organization migration. */ export type OrganizationMigration = Node & { @@ -18447,28 +18339,27 @@ export type OrganizationMigration = Node & { }; /** The Octoshift Organization migration state. */ -export enum OrganizationMigrationState { +export type OrganizationMigrationState = /** The Octoshift migration has failed. */ - Failed = 'FAILED', + | 'FAILED' /** The Octoshift migration has invalid credentials. */ - FailedValidation = 'FAILED_VALIDATION', + | 'FAILED_VALIDATION' /** The Octoshift migration is in progress. */ - InProgress = 'IN_PROGRESS', + | 'IN_PROGRESS' /** The Octoshift migration has not started. */ - NotStarted = 'NOT_STARTED', + | 'NOT_STARTED' /** The Octoshift migration needs to have its credentials validated. */ - PendingValidation = 'PENDING_VALIDATION', + | 'PENDING_VALIDATION' /** The Octoshift migration is performing post repository migrations. */ - PostRepoMigration = 'POST_REPO_MIGRATION', + | 'POST_REPO_MIGRATION' /** The Octoshift migration is performing pre repository migrations. */ - PreRepoMigration = 'PRE_REPO_MIGRATION', + | 'PRE_REPO_MIGRATION' /** The Octoshift migration has been queued. */ - Queued = 'QUEUED', + | 'QUEUED' /** The Octoshift org migration is performing repository migrations. */ - RepoMigration = 'REPO_MIGRATION', + | 'REPO_MIGRATION' /** The Octoshift migration has succeeded. */ - Succeeded = 'SUCCEEDED' -} + | 'SUCCEEDED'; /** Used for argument of CreateProjectV2 mutation. */ export type OrganizationOrUser = Organization | User; @@ -18482,12 +18373,11 @@ export type OrganizationOrder = { }; /** Properties by which organization connections can be ordered. */ -export enum OrganizationOrderField { +export type OrganizationOrderField = /** Order organizations by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order organizations by login */ - Login = 'LOGIN' -} + | 'LOGIN'; /** Parameters to be used for the organization_property condition */ export type OrganizationPropertyConditionTarget = { @@ -18685,10 +18575,9 @@ export type PackageFileOrder = { }; /** Properties by which package file connections can be ordered. */ -export enum PackageFileOrderField { +export type PackageFileOrderField = /** Order package files by creation time */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** Ways in which lists of packages can be ordered upon return. */ export type PackageOrder = { @@ -18699,10 +18588,9 @@ export type PackageOrder = { }; /** Properties by which package connections can be ordered. */ -export enum PackageOrderField { +export type PackageOrderField = /** Order packages by creation time */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** Represents an owner of a package. */ export type PackageOwner = { @@ -18744,37 +18632,21 @@ export type PackageTag = Node & { }; /** The possible types of a package. */ -export enum PackageType { +export type PackageType = /** A debian package. */ - Debian = 'DEBIAN', - /** - * A docker image. - * @deprecated DOCKER will be removed from this enum as this type will be migrated to only be used by the Packages REST API. Removal on 2021-06-21 UTC. - */ - Docker = 'DOCKER', - /** - * A maven package. - * @deprecated MAVEN will be removed from this enum as this type will be migrated to only be used by the Packages REST API. Removal on 2023-02-10 UTC. - */ - Maven = 'MAVEN', - /** - * An npm package. - * @deprecated NPM will be removed from this enum as this type will be migrated to only be used by the Packages REST API. Removal on 2022-11-21 UTC. - */ - Npm = 'NPM', - /** - * A nuget package. - * @deprecated NUGET will be removed from this enum as this type will be migrated to only be used by the Packages REST API. Removal on 2022-11-21 UTC. - */ - Nuget = 'NUGET', + | 'DEBIAN' + /** A docker image. */ + | 'DOCKER' + /** A maven package. */ + | 'MAVEN' + /** An npm package. */ + | 'NPM' + /** A nuget package. */ + | 'NUGET' /** A python package. */ - Pypi = 'PYPI', - /** - * A rubygems package. - * @deprecated RUBYGEMS will be removed from this enum as this type will be migrated to only be used by the Packages REST API. Removal on 2022-12-28 UTC. - */ - Rubygems = 'RUBYGEMS' -} + | 'PYPI' + /** A rubygems package. */ + | 'RUBYGEMS'; /** Information about a specific package version. */ export type PackageVersion = Node & { @@ -18842,10 +18714,9 @@ export type PackageVersionOrder = { }; /** Properties by which package version connections can be ordered. */ -export enum PackageVersionOrderField { +export type PackageVersionOrderField = /** Order package versions by creation time */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** Represents a object that contains package version activity statistics such as downloads. */ export type PackageVersionStatistics = { @@ -18894,20 +18765,19 @@ export type ParentIssueRemovedEvent = Node & { }; /** The possible types of patch statuses. */ -export enum PatchStatus { +export type PatchStatus = /** The file was added. Git status 'A'. */ - Added = 'ADDED', + | 'ADDED' /** The file's type was changed. Git status 'T'. */ - Changed = 'CHANGED', + | 'CHANGED' /** The file was copied. Git status 'C'. */ - Copied = 'COPIED', + | 'COPIED' /** The file was deleted. Git status 'D'. */ - Deleted = 'DELETED', + | 'DELETED' /** The file's contents were changed. Git status 'M'. */ - Modified = 'MODIFIED', + | 'MODIFIED' /** The file was renamed. Git status 'R'. */ - Renamed = 'RENAMED' -} + | 'RENAMED'; /** Types that can grant permissions on a repository to a user */ export type PermissionGranter = Organization | Repository | Team; @@ -18989,24 +18859,23 @@ export type PinnableItemEdge = { }; /** Represents items that can be pinned to a profile page or dashboard. */ -export enum PinnableItemType { +export type PinnableItemType = /** A gist. */ - Gist = 'GIST', + | 'GIST' /** An issue. */ - Issue = 'ISSUE', + | 'ISSUE' /** An organization. */ - Organization = 'ORGANIZATION', + | 'ORGANIZATION' /** A project. */ - Project = 'PROJECT', + | 'PROJECT' /** A pull request. */ - PullRequest = 'PULL_REQUEST', + | 'PULL_REQUEST' /** A repository. */ - Repository = 'REPOSITORY', + | 'REPOSITORY' /** A team. */ - Team = 'TEAM', + | 'TEAM' /** A user. */ - User = 'USER' -} + | 'USER'; /** A Pinned Discussion is a discussion pinned to a repository's index page. */ export type PinnedDiscussion = Node & RepositoryNode & { @@ -19056,34 +18925,32 @@ export type PinnedDiscussionEdge = { }; /** Preconfigured gradients that may be used to style discussions pinned within a repository. */ -export enum PinnedDiscussionGradient { +export type PinnedDiscussionGradient = /** A gradient of blue to mint */ - BlueMint = 'BLUE_MINT', + | 'BLUE_MINT' /** A gradient of blue to purple */ - BluePurple = 'BLUE_PURPLE', + | 'BLUE_PURPLE' /** A gradient of pink to blue */ - PinkBlue = 'PINK_BLUE', + | 'PINK_BLUE' /** A gradient of purple to coral */ - PurpleCoral = 'PURPLE_CORAL', + | 'PURPLE_CORAL' /** A gradient of red to orange */ - RedOrange = 'RED_ORANGE' -} + | 'RED_ORANGE'; /** Preconfigured background patterns that may be used to style discussions pinned within a repository. */ -export enum PinnedDiscussionPattern { +export type PinnedDiscussionPattern = /** An upward-facing chevron pattern */ - ChevronUp = 'CHEVRON_UP', + | 'CHEVRON_UP' /** A hollow dot pattern */ - Dot = 'DOT', + | 'DOT' /** A solid dot pattern */ - DotFill = 'DOT_FILL', + | 'DOT_FILL' /** A heart pattern */ - HeartFill = 'HEART_FILL', + | 'HEART_FILL' /** A plus sign pattern */ - Plus = 'PLUS', + | 'PLUS' /** A lightning bolt pattern */ - Zap = 'ZAP' -} + | 'ZAP'; /** Represents a pinned environment on a given repository */ export type PinnedEnvironment = Node & { @@ -19133,10 +19000,9 @@ export type PinnedEnvironmentOrder = { }; /** Properties by which pinned environments connections can be ordered */ -export enum PinnedEnvironmentOrderField { +export type PinnedEnvironmentOrderField = /** Order pinned environments by position */ - Position = 'POSITION' -} + | 'POSITION'; /** Represents a 'pinned' event on a given issue or pull request. */ export type PinnedEvent = Node & { @@ -19663,18 +19529,11 @@ export type ProjectCard = Node & { }; /** The possible archived states of a project card. */ -export enum ProjectCardArchivedState { - /** - * A project card that is archived - * @deprecated Projects (classic) is being deprecated in favor of the new Projects experience, see: https://github.blog/changelog/2024-05-23-sunset-notice-projects-classic/. Removal on 2025-04-01 UTC. - */ - Archived = 'ARCHIVED', - /** - * A project card that is not archived - * @deprecated Projects (classic) is being deprecated in favor of the new Projects experience, see: https://github.blog/changelog/2024-05-23-sunset-notice-projects-classic/. Removal on 2025-04-01 UTC. - */ - NotArchived = 'NOT_ARCHIVED' -} +export type ProjectCardArchivedState = + /** A project card that is archived */ + | 'ARCHIVED' + /** A project card that is not archived */ + | 'NOT_ARCHIVED'; /** The connection type for ProjectCard. */ export type ProjectCardConnection = { @@ -19710,14 +19569,13 @@ export type ProjectCardImport = { export type ProjectCardItem = Issue | PullRequest; /** Various content states of a ProjectCard */ -export enum ProjectCardState { +export type ProjectCardState = /** The card has content only. */ - ContentOnly = 'CONTENT_ONLY', + | 'CONTENT_ONLY' /** The card has a note only. */ - NoteOnly = 'NOTE_ONLY', + | 'NOTE_ONLY' /** The card is redacted. */ - Redacted = 'REDACTED' -} + | 'REDACTED'; /** A column inside a project. */ export type ProjectColumn = Node & { @@ -19817,14 +19675,13 @@ export type ProjectColumnImport = { }; /** The semantic purpose of the column - todo, in progress, or done. */ -export enum ProjectColumnPurpose { +export type ProjectColumnPurpose = /** The column contains cards which are complete */ - Done = 'DONE', + | 'DONE' /** The column contains cards which are currently being worked on */ - InProgress = 'IN_PROGRESS', + | 'IN_PROGRESS' /** The column contains cards still to be worked on */ - Todo = 'TODO' -} + | 'TODO'; /** A list of projects associated with the owner. */ export type ProjectConnection = { @@ -19857,14 +19714,13 @@ export type ProjectOrder = { }; /** Properties by which project connections can be ordered. */ -export enum ProjectOrderField { +export type ProjectOrderField = /** Order projects by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order projects by name */ - Name = 'NAME', + | 'NAME' /** Order projects by update time */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** Represents an owner of a Project. */ export type ProjectOwner = { @@ -19959,24 +19815,22 @@ export type ProjectProgress = { }; /** State of the project; either 'open' or 'closed' */ -export enum ProjectState { +export type ProjectState = /** The project is closed. */ - Closed = 'CLOSED', + | 'CLOSED' /** The project is open. */ - Open = 'OPEN' -} + | 'OPEN'; /** GitHub-provided templates for Projects */ -export enum ProjectTemplate { +export type ProjectTemplate = /** Create a board with v2 triggers to automatically move cards across To do, In progress and Done columns. */ - AutomatedKanbanV2 = 'AUTOMATED_KANBAN_V2', + | 'AUTOMATED_KANBAN_V2' /** Create a board with triggers to automatically move cards across columns with review automation. */ - AutomatedReviewsKanban = 'AUTOMATED_REVIEWS_KANBAN', + | 'AUTOMATED_REVIEWS_KANBAN' /** Create a board with columns for To do, In progress and Done. */ - BasicKanban = 'BASIC_KANBAN', + | 'BASIC_KANBAN' /** Create a board to triage and prioritize bugs with To do, priority, and Done columns. */ - BugTriage = 'BUG_TRIAGE' -} + | 'BUG_TRIAGE'; /** New projects that manage issues, pull requests and drafts using tables and boards. */ export type ProjectV2 = Closable & Node & Updatable & { @@ -20184,18 +20038,17 @@ export type ProjectV2Connection = { }; /** The type of a project field. */ -export enum ProjectV2CustomFieldType { +export type ProjectV2CustomFieldType = /** Date */ - Date = 'DATE', + | 'DATE' /** Iteration */ - Iteration = 'ITERATION', + | 'ITERATION' /** Number */ - Number = 'NUMBER', + | 'NUMBER' /** Single Select */ - SingleSelect = 'SINGLE_SELECT', + | 'SINGLE_SELECT' /** Text */ - Text = 'TEXT' -} + | 'TEXT'; /** An edge in a connection. */ export type ProjectV2Edge = { @@ -20307,52 +20160,50 @@ export type ProjectV2FieldOrder = { }; /** Properties by which project v2 field connections can be ordered. */ -export enum ProjectV2FieldOrderField { +export type ProjectV2FieldOrderField = /** Order project v2 fields by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order project v2 fields by name */ - Name = 'NAME', + | 'NAME' /** Order project v2 fields by position */ - Position = 'POSITION' -} + | 'POSITION'; /** The type of a project field. */ -export enum ProjectV2FieldType { +export type ProjectV2FieldType = /** Assignees */ - Assignees = 'ASSIGNEES', + | 'ASSIGNEES' /** Date */ - Date = 'DATE', + | 'DATE' /** Issue type */ - IssueType = 'ISSUE_TYPE', + | 'ISSUE_TYPE' /** Iteration */ - Iteration = 'ITERATION', + | 'ITERATION' /** Labels */ - Labels = 'LABELS', + | 'LABELS' /** Linked Pull Requests */ - LinkedPullRequests = 'LINKED_PULL_REQUESTS', + | 'LINKED_PULL_REQUESTS' /** Milestone */ - Milestone = 'MILESTONE', + | 'MILESTONE' /** Number */ - Number = 'NUMBER', + | 'NUMBER' /** Parent issue */ - ParentIssue = 'PARENT_ISSUE', + | 'PARENT_ISSUE' /** Repository */ - Repository = 'REPOSITORY', + | 'REPOSITORY' /** Reviewers */ - Reviewers = 'REVIEWERS', + | 'REVIEWERS' /** Single Select */ - SingleSelect = 'SINGLE_SELECT', + | 'SINGLE_SELECT' /** Sub-issues progress */ - SubIssuesProgress = 'SUB_ISSUES_PROGRESS', + | 'SUB_ISSUES_PROGRESS' /** Text */ - Text = 'TEXT', + | 'TEXT' /** Title */ - Title = 'TITLE', + | 'TITLE' /** Tracked by */ - TrackedBy = 'TRACKED_BY', + | 'TRACKED_BY' /** Tracks */ - Tracks = 'TRACKS' -} + | 'TRACKS'; /** The values that can be used to update a field of an item inside a Project. Only 1 value can be updated at a time. */ export type ProjectV2FieldValue = { @@ -20713,10 +20564,9 @@ export type ProjectV2ItemFieldValueOrder = { }; /** Properties by which project v2 item field value connections can be ordered. */ -export enum ProjectV2ItemFieldValueOrderField { +export type ProjectV2ItemFieldValueOrderField = /** Order project v2 item field values by the their position in the project */ - Position = 'POSITION' -} + | 'POSITION'; /** Ordering options for project v2 item connections */ export type ProjectV2ItemOrder = { @@ -20727,10 +20577,9 @@ export type ProjectV2ItemOrder = { }; /** Properties by which project v2 item connections can be ordered. */ -export enum ProjectV2ItemOrderField { +export type ProjectV2ItemOrderField = /** Order project v2 items by the their position in the project */ - Position = 'POSITION' -} + | 'POSITION'; /** Represents a 'project_v2_item_status_changed' event on a given issue or pull request. */ export type ProjectV2ItemStatusChangedEvent = Node & ProjectV2Event & { @@ -20752,16 +20601,15 @@ export type ProjectV2ItemStatusChangedEvent = Node & ProjectV2Event & { }; /** The type of a project item. */ -export enum ProjectV2ItemType { +export type ProjectV2ItemType = /** Draft Issue */ - DraftIssue = 'DRAFT_ISSUE', + | 'DRAFT_ISSUE' /** Issue */ - Issue = 'ISSUE', + | 'ISSUE' /** Pull Request */ - PullRequest = 'PULL_REQUEST', + | 'PULL_REQUEST' /** Redacted Item */ - Redacted = 'REDACTED' -} + | 'REDACTED'; /** Represents an iteration */ export type ProjectV2Iteration = { @@ -20841,16 +20689,15 @@ export type ProjectV2Order = { }; /** Properties by which projects can be ordered. */ -export enum ProjectV2OrderField { +export type ProjectV2OrderField = /** The project's date and time of creation */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** The project's number */ - Number = 'NUMBER', + | 'NUMBER' /** The project's title */ - Title = 'TITLE', + | 'TITLE' /** The project's date and time of update */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** Represents an owner of a project. */ export type ProjectV2Owner = { @@ -20881,14 +20728,13 @@ export type ProjectV2OwnerProjectsV2Args = { }; /** The possible roles of a collaborator on a project. */ -export enum ProjectV2PermissionLevel { +export type ProjectV2PermissionLevel = /** The collaborator can view, edit, and maange the settings of the project */ - Admin = 'ADMIN', + | 'ADMIN' /** The collaborator can view the project */ - Read = 'READ', + | 'READ' /** The collaborator can view and edit the project */ - Write = 'WRITE' -} + | 'WRITE'; /** Recent projects for the owner. */ export type ProjectV2Recent = { @@ -20906,16 +20752,15 @@ export type ProjectV2RecentRecentProjectsArgs = { }; /** The possible roles of a collaborator on a project. */ -export enum ProjectV2Roles { +export type ProjectV2Roles = /** The collaborator can view, edit, and maange the settings of the project */ - Admin = 'ADMIN', + | 'ADMIN' /** The collaborator has no direct access to the project */ - None = 'NONE', + | 'NONE' /** The collaborator can view the project */ - Reader = 'READER', + | 'READER' /** The collaborator can view and edit the project */ - Writer = 'WRITER' -} + | 'WRITER'; /** A single select field inside a project. */ export type ProjectV2SingleSelectField = Node & ProjectV2FieldCommon & { @@ -20962,24 +20807,23 @@ export type ProjectV2SingleSelectFieldOption = { }; /** The display color of a single-select field option. */ -export enum ProjectV2SingleSelectFieldOptionColor { +export type ProjectV2SingleSelectFieldOptionColor = /** BLUE */ - Blue = 'BLUE', + | 'BLUE' /** GRAY */ - Gray = 'GRAY', + | 'GRAY' /** GREEN */ - Green = 'GREEN', + | 'GREEN' /** ORANGE */ - Orange = 'ORANGE', + | 'ORANGE' /** PINK */ - Pink = 'PINK', + | 'PINK' /** PURPLE */ - Purple = 'PURPLE', + | 'PURPLE' /** RED */ - Red = 'RED', + | 'RED' /** YELLOW */ - Yellow = 'YELLOW' -} + | 'YELLOW'; /** Represents a single select field option */ export type ProjectV2SingleSelectFieldOptionInput = { @@ -21054,12 +20898,11 @@ export type ProjectV2SortByFieldEdge = { }; /** The possible states of a project v2. */ -export enum ProjectV2State { +export type ProjectV2State = /** A project v2 that has been closed */ - Closed = 'CLOSED', + | 'CLOSED' /** A project v2 that is still open */ - Open = 'OPEN' -} + | 'OPEN'; /** Ways in which project v2 status updates can be ordered. */ export type ProjectV2StatusOrder = { @@ -21124,24 +20967,22 @@ export type ProjectV2StatusUpdateEdge = { }; /** Properties by which project v2 status updates can be ordered. */ -export enum ProjectV2StatusUpdateOrderField { +export type ProjectV2StatusUpdateOrderField = /** Allows chronological ordering of project v2 status updates. */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** The possible statuses of a project v2. */ -export enum ProjectV2StatusUpdateStatus { +export type ProjectV2StatusUpdateStatus = /** A project v2 that is at risk and encountering some challenges. */ - AtRisk = 'AT_RISK', + | 'AT_RISK' /** A project v2 that is complete. */ - Complete = 'COMPLETE', + | 'COMPLETE' /** A project v2 that is inactive. */ - Inactive = 'INACTIVE', + | 'INACTIVE' /** A project v2 that is off track and needs attention. */ - OffTrack = 'OFF_TRACK', + | 'OFF_TRACK' /** A project v2 that is on track with no risks. */ - OnTrack = 'ON_TRACK' -} + | 'ON_TRACK'; /** A view within a ProjectV2. */ export type ProjectV2View = Node & { @@ -21300,14 +21141,13 @@ export type ProjectV2ViewEdge = { }; /** The layout of a project v2 view. */ -export enum ProjectV2ViewLayout { +export type ProjectV2ViewLayout = /** Board layout */ - BoardLayout = 'BOARD_LAYOUT', + | 'BOARD_LAYOUT' /** Roadmap layout */ - RoadmapLayout = 'ROADMAP_LAYOUT', + | 'ROADMAP_LAYOUT' /** Table layout */ - TableLayout = 'TABLE_LAYOUT' -} + | 'TABLE_LAYOUT'; /** Ordering options for project v2 view connections */ export type ProjectV2ViewOrder = { @@ -21318,14 +21158,13 @@ export type ProjectV2ViewOrder = { }; /** Properties by which project v2 view connections can be ordered. */ -export enum ProjectV2ViewOrderField { +export type ProjectV2ViewOrderField = /** Order project v2 views by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order project v2 views by name */ - Name = 'NAME', + | 'NAME' /** Order project v2 views by position */ - Position = 'POSITION' -} + | 'POSITION'; /** A workflow inside a project. */ export type ProjectV2Workflow = Node & { @@ -21384,16 +21223,15 @@ export type ProjectV2WorkflowOrder = { }; /** Properties by which project workflows can be ordered. */ -export enum ProjectV2WorkflowsOrderField { +export type ProjectV2WorkflowsOrderField = /** The date and time of the workflow creation */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** The name of the workflow */ - Name = 'NAME', + | 'NAME' /** The number of the workflow */ - Number = 'NUMBER', + | 'NUMBER' /** The date and time of the workflow update */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** Autogenerated input type of PromoteRepositoryCustomProperty */ export type PromoteRepositoryCustomPropertyInput = { @@ -21971,22 +21809,20 @@ export type PullRequestViewerMergeHeadlineTextArgs = { }; /** Array of allowed merge methods. Allowed values include `merge`, `squash`, and `rebase`. At least one option must be enabled. */ -export enum PullRequestAllowedMergeMethods { +export type PullRequestAllowedMergeMethods = /** Add all commits from the head branch to the base branch with a merge commit. */ - Merge = 'MERGE', + | 'MERGE' /** Add all commits from the head branch onto the base branch individually. */ - Rebase = 'REBASE', + | 'REBASE' /** Combine all commits from the head branch into a single commit in the base branch. */ - Squash = 'SQUASH' -} + | 'SQUASH'; /** The possible methods for updating a pull request's head branch with the base branch. */ -export enum PullRequestBranchUpdateMethod { +export type PullRequestBranchUpdateMethod = /** Update branch via merge */ - Merge = 'MERGE', + | 'MERGE' /** Update branch via rebase */ - Rebase = 'REBASE' -} + | 'REBASE'; /** A file changed in a pull request. */ export type PullRequestChangedFile = { @@ -22132,14 +21968,13 @@ export type PullRequestEdge = { }; /** Represents available types of methods to use when merging a pull request. */ -export enum PullRequestMergeMethod { +export type PullRequestMergeMethod = /** Add all commits from the head branch to the base branch with a merge commit. */ - Merge = 'MERGE', + | 'MERGE' /** Add all commits from the head branch onto the base branch individually. */ - Rebase = 'REBASE', + | 'REBASE' /** Combine all commits from the head branch into a single commit in the base branch. */ - Squash = 'SQUASH' -} + | 'SQUASH'; /** Ways in which lists of issues can be ordered upon return. */ export type PullRequestOrder = { @@ -22150,12 +21985,11 @@ export type PullRequestOrder = { }; /** Properties by which pull_requests connections can be ordered. */ -export enum PullRequestOrderField { +export type PullRequestOrderField = /** Order pull_requests by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order pull_requests by update time */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** Require all commits be made to a non-target branch and submitted via a pull request before they can be merged. */ export type PullRequestParameters = { @@ -22465,12 +22299,11 @@ export type PullRequestReviewCommentEdge = { }; /** The possible states of a pull request review comment. */ -export enum PullRequestReviewCommentState { +export type PullRequestReviewCommentState = /** A comment that is part of a pending review */ - Pending = 'PENDING', + | 'PENDING' /** A comment that is part of a submitted review */ - Submitted = 'SUBMITTED' -} + | 'SUBMITTED'; /** The connection type for PullRequestReview. */ export type PullRequestReviewConnection = { @@ -22505,14 +22338,13 @@ export type PullRequestReviewContributionsByRepositoryContributionsArgs = { }; /** The review status of a pull request. */ -export enum PullRequestReviewDecision { +export type PullRequestReviewDecision = /** The pull request has received an approving review. */ - Approved = 'APPROVED', + | 'APPROVED' /** Changes have been requested on the pull request. */ - ChangesRequested = 'CHANGES_REQUESTED', + | 'CHANGES_REQUESTED' /** A review is required before the pull request can be merged. */ - ReviewRequired = 'REVIEW_REQUIRED' -} + | 'REVIEW_REQUIRED'; /** An edge in a connection. */ export type PullRequestReviewEdge = { @@ -22524,30 +22356,28 @@ export type PullRequestReviewEdge = { }; /** The possible events to perform on a pull request review. */ -export enum PullRequestReviewEvent { +export type PullRequestReviewEvent = /** Submit feedback and approve merging these changes. */ - Approve = 'APPROVE', + | 'APPROVE' /** Submit general feedback without explicit approval. */ - Comment = 'COMMENT', + | 'COMMENT' /** Dismiss review so it now longer effects merging. */ - Dismiss = 'DISMISS', + | 'DISMISS' /** Submit feedback that must be addressed before merging. */ - RequestChanges = 'REQUEST_CHANGES' -} + | 'REQUEST_CHANGES'; /** The possible states of a pull request review. */ -export enum PullRequestReviewState { +export type PullRequestReviewState = /** A review allowing the pull request to merge. */ - Approved = 'APPROVED', + | 'APPROVED' /** A review blocking the pull request from merging. */ - ChangesRequested = 'CHANGES_REQUESTED', + | 'CHANGES_REQUESTED' /** An informational review. */ - Commented = 'COMMENTED', + | 'COMMENTED' /** A review that has been dismissed. */ - Dismissed = 'DISMISSED', + | 'DISMISSED' /** A review that has not yet been submitted. */ - Pending = 'PENDING' -} + | 'PENDING'; /** A threaded list of comments for a given pull request. */ export type PullRequestReviewThread = Node & { @@ -22625,12 +22455,11 @@ export type PullRequestReviewThreadEdge = { }; /** The possible subject types of a pull request review comment. */ -export enum PullRequestReviewThreadSubjectType { +export type PullRequestReviewThreadSubjectType = /** A comment that has been made against the file of a pull request */ - File = 'FILE', + | 'FILE' /** A comment that has been made against the line of a pull request */ - Line = 'LINE' -} + | 'LINE'; /** Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits. */ export type PullRequestRevisionMarker = { @@ -22644,14 +22473,13 @@ export type PullRequestRevisionMarker = { }; /** The possible states of a pull request. */ -export enum PullRequestState { +export type PullRequestState = /** A pull request that has been closed without being merged. */ - Closed = 'CLOSED', + | 'CLOSED' /** A pull request that has been closed by being merged. */ - Merged = 'MERGED', + | 'MERGED' /** A pull request that is still open. */ - Open = 'OPEN' -} + | 'OPEN'; /** A repository pull request template. */ export type PullRequestTemplate = { @@ -22770,168 +22598,166 @@ export type PullRequestTimelineItemsEdge = { }; /** The possible item types found in a timeline. */ -export enum PullRequestTimelineItemsItemType { +export type PullRequestTimelineItemsItemType = /** Represents an 'added_to_merge_queue' event on a given pull request. */ - AddedToMergeQueueEvent = 'ADDED_TO_MERGE_QUEUE_EVENT', + | 'ADDED_TO_MERGE_QUEUE_EVENT' /** Represents a 'added_to_project' event on a given issue or pull request. */ - AddedToProjectEvent = 'ADDED_TO_PROJECT_EVENT', + | 'ADDED_TO_PROJECT_EVENT' /** Represents a 'added_to_project_v2' event on a given issue or pull request. */ - AddedToProjectV2Event = 'ADDED_TO_PROJECT_V2_EVENT', + | 'ADDED_TO_PROJECT_V2_EVENT' /** Represents an 'assigned' event on any assignable object. */ - AssignedEvent = 'ASSIGNED_EVENT', + | 'ASSIGNED_EVENT' /** Represents a 'automatic_base_change_failed' event on a given pull request. */ - AutomaticBaseChangeFailedEvent = 'AUTOMATIC_BASE_CHANGE_FAILED_EVENT', + | 'AUTOMATIC_BASE_CHANGE_FAILED_EVENT' /** Represents a 'automatic_base_change_succeeded' event on a given pull request. */ - AutomaticBaseChangeSucceededEvent = 'AUTOMATIC_BASE_CHANGE_SUCCEEDED_EVENT', + | 'AUTOMATIC_BASE_CHANGE_SUCCEEDED_EVENT' /** Represents a 'auto_merge_disabled' event on a given pull request. */ - AutoMergeDisabledEvent = 'AUTO_MERGE_DISABLED_EVENT', + | 'AUTO_MERGE_DISABLED_EVENT' /** Represents a 'auto_merge_enabled' event on a given pull request. */ - AutoMergeEnabledEvent = 'AUTO_MERGE_ENABLED_EVENT', + | 'AUTO_MERGE_ENABLED_EVENT' /** Represents a 'auto_rebase_enabled' event on a given pull request. */ - AutoRebaseEnabledEvent = 'AUTO_REBASE_ENABLED_EVENT', + | 'AUTO_REBASE_ENABLED_EVENT' /** Represents a 'auto_squash_enabled' event on a given pull request. */ - AutoSquashEnabledEvent = 'AUTO_SQUASH_ENABLED_EVENT', + | 'AUTO_SQUASH_ENABLED_EVENT' /** Represents a 'base_ref_changed' event on a given issue or pull request. */ - BaseRefChangedEvent = 'BASE_REF_CHANGED_EVENT', + | 'BASE_REF_CHANGED_EVENT' /** Represents a 'base_ref_deleted' event on a given pull request. */ - BaseRefDeletedEvent = 'BASE_REF_DELETED_EVENT', + | 'BASE_REF_DELETED_EVENT' /** Represents a 'base_ref_force_pushed' event on a given pull request. */ - BaseRefForcePushedEvent = 'BASE_REF_FORCE_PUSHED_EVENT', + | 'BASE_REF_FORCE_PUSHED_EVENT' /** Represents a 'blocked_by_added' event on a given issue. */ - BlockedByAddedEvent = 'BLOCKED_BY_ADDED_EVENT', + | 'BLOCKED_BY_ADDED_EVENT' /** Represents a 'blocked_by_removed' event on a given issue. */ - BlockedByRemovedEvent = 'BLOCKED_BY_REMOVED_EVENT', + | 'BLOCKED_BY_REMOVED_EVENT' /** Represents a 'blocking_added' event on a given issue. */ - BlockingAddedEvent = 'BLOCKING_ADDED_EVENT', + | 'BLOCKING_ADDED_EVENT' /** Represents a 'blocking_removed' event on a given issue. */ - BlockingRemovedEvent = 'BLOCKING_REMOVED_EVENT', + | 'BLOCKING_REMOVED_EVENT' /** Represents a 'closed' event on any `Closable`. */ - ClosedEvent = 'CLOSED_EVENT', + | 'CLOSED_EVENT' /** Represents a 'comment_deleted' event on a given issue or pull request. */ - CommentDeletedEvent = 'COMMENT_DELETED_EVENT', + | 'COMMENT_DELETED_EVENT' /** Represents a 'connected' event on a given issue or pull request. */ - ConnectedEvent = 'CONNECTED_EVENT', + | 'CONNECTED_EVENT' /** Represents a 'converted_from_draft' event on a given issue or pull request. */ - ConvertedFromDraftEvent = 'CONVERTED_FROM_DRAFT_EVENT', + | 'CONVERTED_FROM_DRAFT_EVENT' /** Represents a 'converted_note_to_issue' event on a given issue or pull request. */ - ConvertedNoteToIssueEvent = 'CONVERTED_NOTE_TO_ISSUE_EVENT', + | 'CONVERTED_NOTE_TO_ISSUE_EVENT' /** Represents a 'converted_to_discussion' event on a given issue. */ - ConvertedToDiscussionEvent = 'CONVERTED_TO_DISCUSSION_EVENT', + | 'CONVERTED_TO_DISCUSSION_EVENT' /** Represents a 'convert_to_draft' event on a given pull request. */ - ConvertToDraftEvent = 'CONVERT_TO_DRAFT_EVENT', + | 'CONVERT_TO_DRAFT_EVENT' /** Represents a mention made by one issue or pull request to another. */ - CrossReferencedEvent = 'CROSS_REFERENCED_EVENT', + | 'CROSS_REFERENCED_EVENT' /** Represents a 'demilestoned' event on a given issue or pull request. */ - DemilestonedEvent = 'DEMILESTONED_EVENT', + | 'DEMILESTONED_EVENT' /** Represents a 'deployed' event on a given pull request. */ - DeployedEvent = 'DEPLOYED_EVENT', + | 'DEPLOYED_EVENT' /** Represents a 'deployment_environment_changed' event on a given pull request. */ - DeploymentEnvironmentChangedEvent = 'DEPLOYMENT_ENVIRONMENT_CHANGED_EVENT', + | 'DEPLOYMENT_ENVIRONMENT_CHANGED_EVENT' /** Represents a 'disconnected' event on a given issue or pull request. */ - DisconnectedEvent = 'DISCONNECTED_EVENT', + | 'DISCONNECTED_EVENT' /** Represents a 'head_ref_deleted' event on a given pull request. */ - HeadRefDeletedEvent = 'HEAD_REF_DELETED_EVENT', + | 'HEAD_REF_DELETED_EVENT' /** Represents a 'head_ref_force_pushed' event on a given pull request. */ - HeadRefForcePushedEvent = 'HEAD_REF_FORCE_PUSHED_EVENT', + | 'HEAD_REF_FORCE_PUSHED_EVENT' /** Represents a 'head_ref_restored' event on a given pull request. */ - HeadRefRestoredEvent = 'HEAD_REF_RESTORED_EVENT', + | 'HEAD_REF_RESTORED_EVENT' /** Represents a comment on an Issue. */ - IssueComment = 'ISSUE_COMMENT', + | 'ISSUE_COMMENT' /** Represents a 'issue_field_added' event on a given issue. */ - IssueFieldAddedEvent = 'ISSUE_FIELD_ADDED_EVENT', + | 'ISSUE_FIELD_ADDED_EVENT' /** Represents a 'issue_field_changed' event on a given issue. */ - IssueFieldChangedEvent = 'ISSUE_FIELD_CHANGED_EVENT', + | 'ISSUE_FIELD_CHANGED_EVENT' /** Represents a 'issue_field_removed' event on a given issue. */ - IssueFieldRemovedEvent = 'ISSUE_FIELD_REMOVED_EVENT', + | 'ISSUE_FIELD_REMOVED_EVENT' /** Represents a 'issue_type_added' event on a given issue. */ - IssueTypeAddedEvent = 'ISSUE_TYPE_ADDED_EVENT', + | 'ISSUE_TYPE_ADDED_EVENT' /** Represents a 'issue_type_changed' event on a given issue. */ - IssueTypeChangedEvent = 'ISSUE_TYPE_CHANGED_EVENT', + | 'ISSUE_TYPE_CHANGED_EVENT' /** Represents a 'issue_type_removed' event on a given issue. */ - IssueTypeRemovedEvent = 'ISSUE_TYPE_REMOVED_EVENT', + | 'ISSUE_TYPE_REMOVED_EVENT' /** Represents a 'labeled' event on a given issue or pull request. */ - LabeledEvent = 'LABELED_EVENT', + | 'LABELED_EVENT' /** Represents a 'locked' event on a given issue or pull request. */ - LockedEvent = 'LOCKED_EVENT', + | 'LOCKED_EVENT' /** Represents a 'marked_as_duplicate' event on a given issue or pull request. */ - MarkedAsDuplicateEvent = 'MARKED_AS_DUPLICATE_EVENT', + | 'MARKED_AS_DUPLICATE_EVENT' /** Represents a 'mentioned' event on a given issue or pull request. */ - MentionedEvent = 'MENTIONED_EVENT', + | 'MENTIONED_EVENT' /** Represents a 'merged' event on a given pull request. */ - MergedEvent = 'MERGED_EVENT', + | 'MERGED_EVENT' /** Represents a 'milestoned' event on a given issue or pull request. */ - MilestonedEvent = 'MILESTONED_EVENT', + | 'MILESTONED_EVENT' /** Represents a 'moved_columns_in_project' event on a given issue or pull request. */ - MovedColumnsInProjectEvent = 'MOVED_COLUMNS_IN_PROJECT_EVENT', + | 'MOVED_COLUMNS_IN_PROJECT_EVENT' /** Represents a 'parent_issue_added' event on a given issue. */ - ParentIssueAddedEvent = 'PARENT_ISSUE_ADDED_EVENT', + | 'PARENT_ISSUE_ADDED_EVENT' /** Represents a 'parent_issue_removed' event on a given issue. */ - ParentIssueRemovedEvent = 'PARENT_ISSUE_REMOVED_EVENT', + | 'PARENT_ISSUE_REMOVED_EVENT' /** Represents a 'pinned' event on a given issue or pull request. */ - PinnedEvent = 'PINNED_EVENT', + | 'PINNED_EVENT' /** Represents a 'project_v2_item_status_changed' event on a given issue or pull request. */ - ProjectV2ItemStatusChangedEvent = 'PROJECT_V2_ITEM_STATUS_CHANGED_EVENT', + | 'PROJECT_V2_ITEM_STATUS_CHANGED_EVENT' /** Represents a Git commit part of a pull request. */ - PullRequestCommit = 'PULL_REQUEST_COMMIT', + | 'PULL_REQUEST_COMMIT' /** Represents a commit comment thread part of a pull request. */ - PullRequestCommitCommentThread = 'PULL_REQUEST_COMMIT_COMMENT_THREAD', + | 'PULL_REQUEST_COMMIT_COMMENT_THREAD' /** A review object for a given pull request. */ - PullRequestReview = 'PULL_REQUEST_REVIEW', + | 'PULL_REQUEST_REVIEW' /** A threaded list of comments for a given pull request. */ - PullRequestReviewThread = 'PULL_REQUEST_REVIEW_THREAD', + | 'PULL_REQUEST_REVIEW_THREAD' /** Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits. */ - PullRequestRevisionMarker = 'PULL_REQUEST_REVISION_MARKER', + | 'PULL_REQUEST_REVISION_MARKER' /** Represents a 'ready_for_review' event on a given pull request. */ - ReadyForReviewEvent = 'READY_FOR_REVIEW_EVENT', + | 'READY_FOR_REVIEW_EVENT' /** Represents a 'referenced' event on a given `ReferencedSubject`. */ - ReferencedEvent = 'REFERENCED_EVENT', + | 'REFERENCED_EVENT' /** Represents a 'removed_from_merge_queue' event on a given pull request. */ - RemovedFromMergeQueueEvent = 'REMOVED_FROM_MERGE_QUEUE_EVENT', + | 'REMOVED_FROM_MERGE_QUEUE_EVENT' /** Represents a 'removed_from_project' event on a given issue or pull request. */ - RemovedFromProjectEvent = 'REMOVED_FROM_PROJECT_EVENT', + | 'REMOVED_FROM_PROJECT_EVENT' /** Represents a 'removed_from_project_v2' event on a given issue or pull request. */ - RemovedFromProjectV2Event = 'REMOVED_FROM_PROJECT_V2_EVENT', + | 'REMOVED_FROM_PROJECT_V2_EVENT' /** Represents a 'renamed' event on a given issue or pull request */ - RenamedTitleEvent = 'RENAMED_TITLE_EVENT', + | 'RENAMED_TITLE_EVENT' /** Represents a 'reopened' event on any `Closable`. */ - ReopenedEvent = 'REOPENED_EVENT', + | 'REOPENED_EVENT' /** Represents a 'review_dismissed' event on a given issue or pull request. */ - ReviewDismissedEvent = 'REVIEW_DISMISSED_EVENT', + | 'REVIEW_DISMISSED_EVENT' /** Represents an 'review_requested' event on a given pull request. */ - ReviewRequestedEvent = 'REVIEW_REQUESTED_EVENT', + | 'REVIEW_REQUESTED_EVENT' /** Represents an 'review_request_removed' event on a given pull request. */ - ReviewRequestRemovedEvent = 'REVIEW_REQUEST_REMOVED_EVENT', + | 'REVIEW_REQUEST_REMOVED_EVENT' /** Represents a 'subscribed' event on a given `Subscribable`. */ - SubscribedEvent = 'SUBSCRIBED_EVENT', + | 'SUBSCRIBED_EVENT' /** Represents a 'sub_issue_added' event on a given issue. */ - SubIssueAddedEvent = 'SUB_ISSUE_ADDED_EVENT', + | 'SUB_ISSUE_ADDED_EVENT' /** Represents a 'sub_issue_removed' event on a given issue. */ - SubIssueRemovedEvent = 'SUB_ISSUE_REMOVED_EVENT', + | 'SUB_ISSUE_REMOVED_EVENT' /** Represents a 'transferred' event on a given issue or pull request. */ - TransferredEvent = 'TRANSFERRED_EVENT', + | 'TRANSFERRED_EVENT' /** Represents an 'unassigned' event on any assignable object. */ - UnassignedEvent = 'UNASSIGNED_EVENT', + | 'UNASSIGNED_EVENT' /** Represents an 'unlabeled' event on a given issue or pull request. */ - UnlabeledEvent = 'UNLABELED_EVENT', + | 'UNLABELED_EVENT' /** Represents an 'unlocked' event on a given issue or pull request. */ - UnlockedEvent = 'UNLOCKED_EVENT', + | 'UNLOCKED_EVENT' /** Represents an 'unmarked_as_duplicate' event on a given issue or pull request. */ - UnmarkedAsDuplicateEvent = 'UNMARKED_AS_DUPLICATE_EVENT', + | 'UNMARKED_AS_DUPLICATE_EVENT' /** Represents an 'unpinned' event on a given issue or pull request. */ - UnpinnedEvent = 'UNPINNED_EVENT', + | 'UNPINNED_EVENT' /** Represents an 'unsubscribed' event on a given `Subscribable`. */ - UnsubscribedEvent = 'UNSUBSCRIBED_EVENT', + | 'UNSUBSCRIBED_EVENT' /** Represents a 'user_blocked' event on a given user. */ - UserBlockedEvent = 'USER_BLOCKED_EVENT' -} + | 'USER_BLOCKED_EVENT'; /** The possible target states when updating a pull request. */ -export enum PullRequestUpdateState { +export type PullRequestUpdateState = /** A pull request that has been closed without being merged. */ - Closed = 'CLOSED', + | 'CLOSED' /** A pull request that is still open. */ - Open = 'OPEN' -} + | 'OPEN'; /** A Git push. */ export type Push = Node & { @@ -23360,24 +23186,23 @@ export type ReactionConnection = { }; /** Emojis that can be attached to Issues, Pull Requests and Comments. */ -export enum ReactionContent { +export type ReactionContent = /** Represents the `:confused:` emoji. */ - Confused = 'CONFUSED', + | 'CONFUSED' /** Represents the `:eyes:` emoji. */ - Eyes = 'EYES', + | 'EYES' /** Represents the `:heart:` emoji. */ - Heart = 'HEART', + | 'HEART' /** Represents the `:hooray:` emoji. */ - Hooray = 'HOORAY', + | 'HOORAY' /** Represents the `:laugh:` emoji. */ - Laugh = 'LAUGH', + | 'LAUGH' /** Represents the `:rocket:` emoji. */ - Rocket = 'ROCKET', + | 'ROCKET' /** Represents the `:-1:` emoji. */ - ThumbsDown = 'THUMBS_DOWN', + | 'THUMBS_DOWN' /** Represents the `:+1:` emoji. */ - ThumbsUp = 'THUMBS_UP' -} + | 'THUMBS_UP'; /** An edge in a connection. */ export type ReactionEdge = { @@ -23435,10 +23260,9 @@ export type ReactionOrder = { }; /** A list of fields that reactions can be ordered by. */ -export enum ReactionOrderField { +export type ReactionOrderField = /** Allows ordering a list of reactions by when they were created. */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** Types that can be assigned to reactions. */ export type Reactor = Bot | Mannequin | Organization | User; @@ -23587,12 +23411,11 @@ export type RefOrder = { }; /** Properties by which ref connections can be ordered. */ -export enum RefOrderField { +export type RefOrderField = /** Order refs by their alphanumeric name */ - Alphabetical = 'ALPHABETICAL', + | 'ALPHABETICAL' /** Order refs by underlying commit date if the ref prefix is refs/tags/ */ - TagCommitDate = 'TAG_COMMIT_DATE' -} + | 'TAG_COMMIT_DATE'; /** A ref update */ export type RefUpdate = { @@ -23887,12 +23710,11 @@ export type ReleaseOrder = { }; /** Properties by which release connections can be ordered. */ -export enum ReleaseOrderField { +export type ReleaseOrderField = /** Order releases by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order releases alphabetically by name */ - Name = 'NAME' -} + | 'NAME'; /** Autogenerated input type of RemoveAssigneesFromAssignable */ export type RemoveAssigneesFromAssignableInput = { @@ -24449,14 +24271,13 @@ export type RepoAccessAuditEntry = AuditEntry & Node & OrganizationAuditEntryDat }; /** The privacy of a repository */ -export enum RepoAccessAuditEntryVisibility { +export type RepoAccessAuditEntryVisibility = /** The repository is visible only to users in the same enterprise. */ - Internal = 'INTERNAL', + | 'INTERNAL' /** The repository is visible only to those with explicit access. */ - Private = 'PRIVATE', + | 'PRIVATE' /** The repository is visible to everyone. */ - Public = 'PUBLIC' -} + | 'PUBLIC'; /** Audit log entry for a repo.add_member event. */ export type RepoAddMemberAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData & { @@ -24564,14 +24385,13 @@ export type RepoAddMemberAuditEntry = AuditEntry & Node & OrganizationAuditEntry }; /** The privacy of a repository */ -export enum RepoAddMemberAuditEntryVisibility { +export type RepoAddMemberAuditEntryVisibility = /** The repository is visible only to users in the same enterprise. */ - Internal = 'INTERNAL', + | 'INTERNAL' /** The repository is visible only to those with explicit access. */ - Private = 'PRIVATE', + | 'PRIVATE' /** The repository is visible to everyone. */ - Public = 'PUBLIC' -} + | 'PUBLIC'; /** Audit log entry for a repo.add_topic event. */ export type RepoAddTopicAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData & TopicAuditEntryData & { @@ -24783,14 +24603,13 @@ export type RepoArchivedAuditEntry = AuditEntry & Node & OrganizationAuditEntryD }; /** The privacy of a repository */ -export enum RepoArchivedAuditEntryVisibility { +export type RepoArchivedAuditEntryVisibility = /** The repository is visible only to users in the same enterprise. */ - Internal = 'INTERNAL', + | 'INTERNAL' /** The repository is visible only to those with explicit access. */ - Private = 'PRIVATE', + | 'PRIVATE' /** The repository is visible to everyone. */ - Public = 'PUBLIC' -} + | 'PUBLIC'; /** Audit log entry for a repo.change_merge_setting event. */ export type RepoChangeMergeSettingAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData & { @@ -24903,14 +24722,13 @@ export type RepoChangeMergeSettingAuditEntry = AuditEntry & Node & OrganizationA }; /** The merge options available for pull requests to this repository. */ -export enum RepoChangeMergeSettingAuditEntryMergeType { +export type RepoChangeMergeSettingAuditEntryMergeType = /** The pull request is added to the base branch in a merge commit. */ - Merge = 'MERGE', + | 'MERGE' /** Commits from the pull request are added onto the base branch individually without a merge commit. */ - Rebase = 'REBASE', + | 'REBASE' /** The pull request's commits are squashed into a single commit before they are merged to the base branch. */ - Squash = 'SQUASH' -} + | 'SQUASH'; /** Audit log entry for a repo.config.disable_anonymous_git_access event. */ export type RepoConfigDisableAnonymousGitAccessAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData & { @@ -26028,14 +25846,13 @@ export type RepoCreateAuditEntry = AuditEntry & Node & OrganizationAuditEntryDat }; /** The privacy of a repository */ -export enum RepoCreateAuditEntryVisibility { +export type RepoCreateAuditEntryVisibility = /** The repository is visible only to users in the same enterprise. */ - Internal = 'INTERNAL', + | 'INTERNAL' /** The repository is visible only to those with explicit access. */ - Private = 'PRIVATE', + | 'PRIVATE' /** The repository is visible to everyone. */ - Public = 'PUBLIC' -} + | 'PUBLIC'; /** Audit log entry for a repo.destroy event. */ export type RepoDestroyAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData & { @@ -26143,14 +25960,13 @@ export type RepoDestroyAuditEntry = AuditEntry & Node & OrganizationAuditEntryDa }; /** The privacy of a repository */ -export enum RepoDestroyAuditEntryVisibility { +export type RepoDestroyAuditEntryVisibility = /** The repository is visible only to users in the same enterprise. */ - Internal = 'INTERNAL', + | 'INTERNAL' /** The repository is visible only to those with explicit access. */ - Private = 'PRIVATE', + | 'PRIVATE' /** The repository is visible to everyone. */ - Public = 'PUBLIC' -} + | 'PUBLIC'; /** Audit log entry for a repo.remove_member event. */ export type RepoRemoveMemberAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData & { @@ -26258,14 +26074,13 @@ export type RepoRemoveMemberAuditEntry = AuditEntry & Node & OrganizationAuditEn }; /** The privacy of a repository */ -export enum RepoRemoveMemberAuditEntryVisibility { +export type RepoRemoveMemberAuditEntryVisibility = /** The repository is visible only to users in the same enterprise. */ - Internal = 'INTERNAL', + | 'INTERNAL' /** The repository is visible only to those with explicit access. */ - Private = 'PRIVATE', + | 'PRIVATE' /** The repository is visible to everyone. */ - Public = 'PUBLIC' -} + | 'PUBLIC'; /** Audit log entry for a repo.remove_topic event. */ export type RepoRemoveTopicAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData & TopicAuditEntryData & { @@ -26372,20 +26187,19 @@ export type RepoRemoveTopicAuditEntry = AuditEntry & Node & OrganizationAuditEnt }; /** The reasons a piece of content can be reported or minimized. */ -export enum ReportedContentClassifiers { +export type ReportedContentClassifiers = /** An abusive or harassing piece of content */ - Abuse = 'ABUSE', + | 'ABUSE' /** A duplicated piece of content */ - Duplicate = 'DUPLICATE', + | 'DUPLICATE' /** An irrelevant piece of content */ - OffTopic = 'OFF_TOPIC', + | 'OFF_TOPIC' /** An outdated piece of content */ - Outdated = 'OUTDATED', + | 'OUTDATED' /** The content has been resolved */ - Resolved = 'RESOLVED', + | 'RESOLVED' /** A spammy piece of content */ - Spam = 'SPAM' -} + | 'SPAM'; /** A repository contains the content for a project. */ export type Repository = Node & PackageOwner & ProjectOwner & ProjectV2Recent & RepositoryInfo & Starrable & Subscribable & UniformResourceLocatable & { @@ -27186,14 +27000,13 @@ export type RepositoryWatchersArgs = { }; /** The affiliation of a user to a repository */ -export enum RepositoryAffiliation { +export type RepositoryAffiliation = /** Repositories that the user has been added to as a collaborator. */ - Collaborator = 'COLLABORATOR', + | 'COLLABORATOR' /** Repositories that the user has access to through being a member of an organization. This includes every repository on every team that the user is on. */ - OrganizationMember = 'ORGANIZATION_MEMBER', + | 'ORGANIZATION_MEMBER' /** Repositories that are owned by the authenticated user. */ - Owner = 'OWNER' -} + | 'OWNER'; /** Metadata for an audit entry with action repo.* */ export type RepositoryAuditEntryData = { @@ -27285,18 +27098,17 @@ export type RepositoryContactLink = { }; /** The reason a repository is listed as 'contributed'. */ -export enum RepositoryContributionType { +export type RepositoryContributionType = /** Created a commit */ - Commit = 'COMMIT', + | 'COMMIT' /** Created an issue */ - Issue = 'ISSUE', + | 'ISSUE' /** Created a pull request */ - PullRequest = 'PULL_REQUEST', + | 'PULL_REQUEST' /** Reviewed a pull request */ - PullRequestReview = 'PULL_REQUEST_REVIEW', + | 'PULL_REQUEST_REVIEW' /** Created the repository */ - Repository = 'REPOSITORY' -} + | 'REPOSITORY'; /** A repository custom property. */ export type RepositoryCustomProperty = Node & { @@ -27377,12 +27189,11 @@ export type RepositoryCustomPropertyValueEdge = { }; /** The allowed actors who can edit the values of a custom property. */ -export enum RepositoryCustomPropertyValuesEditableBy { +export type RepositoryCustomPropertyValuesEditableBy = /** The organization actors. */ - OrgActors = 'ORG_ACTORS', + | 'ORG_ACTORS' /** The organization and repository actors. */ - OrgAndRepoActors = 'ORG_AND_REPO_ACTORS' -} + | 'ORG_AND_REPO_ACTORS'; /** Represents an author of discussions in repositories. */ export type RepositoryDiscussionAuthor = { @@ -27528,40 +27339,37 @@ export type RepositoryInteractionAbility = { }; /** A repository interaction limit. */ -export enum RepositoryInteractionLimit { +export type RepositoryInteractionLimit = /** Users that are not collaborators will not be able to interact with the repository. */ - CollaboratorsOnly = 'COLLABORATORS_ONLY', + | 'COLLABORATORS_ONLY' /** Users that have not previously committed to a repository’s default branch will be unable to interact with the repository. */ - ContributorsOnly = 'CONTRIBUTORS_ONLY', + | 'CONTRIBUTORS_ONLY' /** Users that have recently created their account will be unable to interact with the repository. */ - ExistingUsers = 'EXISTING_USERS', + | 'EXISTING_USERS' /** No interaction limits are enabled. */ - NoLimit = 'NO_LIMIT' -} + | 'NO_LIMIT'; /** The length for a repository interaction limit to be enabled for. */ -export enum RepositoryInteractionLimitExpiry { +export type RepositoryInteractionLimitExpiry = /** The interaction limit will expire after 1 day. */ - OneDay = 'ONE_DAY', + | 'ONE_DAY' /** The interaction limit will expire after 1 month. */ - OneMonth = 'ONE_MONTH', + | 'ONE_MONTH' /** The interaction limit will expire after 1 week. */ - OneWeek = 'ONE_WEEK', + | 'ONE_WEEK' /** The interaction limit will expire after 6 months. */ - SixMonths = 'SIX_MONTHS', + | 'SIX_MONTHS' /** The interaction limit will expire after 3 days. */ - ThreeDays = 'THREE_DAYS' -} + | 'THREE_DAYS'; /** Indicates where an interaction limit is configured. */ -export enum RepositoryInteractionLimitOrigin { +export type RepositoryInteractionLimitOrigin = /** A limit that is configured at the organization level. */ - Organization = 'ORGANIZATION', + | 'ORGANIZATION' /** A limit that is configured at the repository level. */ - Repository = 'REPOSITORY', + | 'REPOSITORY' /** A limit that is configured at the user-wide level. */ - User = 'USER' -} + | 'USER'; /** An invitation for a user to be added to a repository. */ export type RepositoryInvitation = Node & { @@ -27613,26 +27421,24 @@ export type RepositoryInvitationOrder = { }; /** Properties by which repository invitation connections can be ordered. */ -export enum RepositoryInvitationOrderField { +export type RepositoryInvitationOrderField = /** Order repository invitations by creation time */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** The possible reasons a given repository could be in a locked state. */ -export enum RepositoryLockReason { +export type RepositoryLockReason = /** The repository is locked due to a billing related reason. */ - Billing = 'BILLING', + | 'BILLING' /** The repository is locked due to a migration. */ - Migrating = 'MIGRATING', + | 'MIGRATING' /** The repository is locked due to a move. */ - Moving = 'MOVING', + | 'MOVING' /** The repository is locked due to a rename. */ - Rename = 'RENAME', + | 'RENAME' /** The repository is locked due to a trade controls related reason. */ - TradeRestriction = 'TRADE_RESTRICTION', + | 'TRADE_RESTRICTION' /** The repository is locked due to an ownership transfer. */ - TransferringOwnership = 'TRANSFERRING_OWNERSHIP' -} + | 'TRANSFERRING_OWNERSHIP'; /** A GitHub Enterprise Importer (GEI) repository migration. */ export type RepositoryMigration = Migration & Node & { @@ -27692,18 +27498,16 @@ export type RepositoryMigrationOrder = { }; /** Possible directions in which to order a list of repository migrations when provided an `orderBy` argument. */ -export enum RepositoryMigrationOrderDirection { +export type RepositoryMigrationOrderDirection = /** Specifies an ascending order for a given `orderBy` argument. */ - Asc = 'ASC', + | 'ASC' /** Specifies a descending order for a given `orderBy` argument. */ - Desc = 'DESC' -} + | 'DESC'; /** Properties by which repository migrations can be ordered. */ -export enum RepositoryMigrationOrderField { +export type RepositoryMigrationOrderField = /** Order mannequins why when they were created. */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** Parameters to be used for the repository_name condition */ export type RepositoryNameConditionTarget = { @@ -27741,18 +27545,17 @@ export type RepositoryOrder = { }; /** Properties by which repository connections can be ordered. */ -export enum RepositoryOrderField { +export type RepositoryOrderField = /** Order repositories by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order repositories by name */ - Name = 'NAME', + | 'NAME' /** Order repositories by push time */ - PushedAt = 'PUSHED_AT', + | 'PUSHED_AT' /** Order repositories by number of stargazers */ - Stargazers = 'STARGAZERS', + | 'STARGAZERS' /** Order repositories by update time */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** Represents an owner of a Repository. */ export type RepositoryOwner = { @@ -27804,18 +27607,17 @@ export type RepositoryOwnerRepositoryArgs = { }; /** The access level to a repository */ -export enum RepositoryPermission { +export type RepositoryPermission = /** Can read, clone, and push to this repository. Can also manage issues, pull requests, and repository settings, including adding collaborators */ - Admin = 'ADMIN', + | 'ADMIN' /** Can read, clone, and push to this repository. They can also manage issues, pull requests, and some repository settings */ - Maintain = 'MAINTAIN', + | 'MAINTAIN' /** Can read and clone this repository. Can also open and comment on issues and pull requests */ - Read = 'READ', + | 'READ' /** Can read and clone this repository. Can also manage issues and pull requests */ - Triage = 'TRIAGE', + | 'TRIAGE' /** Can read, clone, and push to this repository. Can also manage issues and pull requests */ - Write = 'WRITE' -} + | 'WRITE'; /** Information about the availability of features and limits for a repository based on its billing plan. */ export type RepositoryPlanFeatures = { @@ -27833,12 +27635,11 @@ export type RepositoryPlanFeatures = { }; /** The privacy of a repository */ -export enum RepositoryPrivacy { +export type RepositoryPrivacy = /** Private */ - Private = 'PRIVATE', + | 'PRIVATE' /** Public */ - Public = 'PUBLIC' -} + | 'PUBLIC'; /** Parameters to be used for the repository_property condition */ export type RepositoryPropertyConditionTarget = { @@ -27940,80 +27741,78 @@ export type RepositoryRuleOrder = { }; /** Properties by which repository rule connections can be ordered. */ -export enum RepositoryRuleOrderField { +export type RepositoryRuleOrderField = /** Order repository rules by created time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order repository rules by type */ - Type = 'TYPE', + | 'TYPE' /** Order repository rules by updated time */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** The rule types supported in rulesets */ -export enum RepositoryRuleType { +export type RepositoryRuleType = /** Authorization */ - Authorization = 'AUTHORIZATION', + | 'AUTHORIZATION' /** Branch name pattern */ - BranchNamePattern = 'BRANCH_NAME_PATTERN', + | 'BRANCH_NAME_PATTERN' /** Choose which tools must provide code scanning results before the reference is updated. When configured, code scanning must be enabled and have results for both the commit and the reference being updated. */ - CodeScanning = 'CODE_SCANNING', + | 'CODE_SCANNING' /** Committer email pattern */ - CommitterEmailPattern = 'COMMITTER_EMAIL_PATTERN', + | 'COMMITTER_EMAIL_PATTERN' /** Commit author email pattern */ - CommitAuthorEmailPattern = 'COMMIT_AUTHOR_EMAIL_PATTERN', + | 'COMMIT_AUTHOR_EMAIL_PATTERN' /** Commit message pattern */ - CommitMessagePattern = 'COMMIT_MESSAGE_PATTERN', + | 'COMMIT_MESSAGE_PATTERN' /** Request Copilot code review for new pull requests automatically if the author has access to Copilot code review and their premium requests quota has not reached the limit. */ - CopilotCodeReview = 'COPILOT_CODE_REVIEW', + | 'COPILOT_CODE_REVIEW' /** Only allow users with bypass permission to create matching refs. */ - Creation = 'CREATION', + | 'CREATION' /** Only allow users with bypass permissions to delete matching refs. */ - Deletion = 'DELETION', + | 'DELETION' /** Prevent commits that include files with specified file extensions from being pushed to the commit graph. */ - FileExtensionRestriction = 'FILE_EXTENSION_RESTRICTION', + | 'FILE_EXTENSION_RESTRICTION' /** Prevent commits that include changes in specified file and folder paths from being pushed to the commit graph. This includes absolute paths that contain file names. */ - FilePathRestriction = 'FILE_PATH_RESTRICTION', + | 'FILE_PATH_RESTRICTION' /** Branch is read-only. Users cannot push to the branch. */ - LockBranch = 'LOCK_BRANCH', + | 'LOCK_BRANCH' /** Prevent commits that include file paths that exceed the specified character limit from being pushed to the commit graph. */ - MaxFilePathLength = 'MAX_FILE_PATH_LENGTH', + | 'MAX_FILE_PATH_LENGTH' /** Prevent commits with individual files that exceed the specified limit from being pushed to the commit graph. */ - MaxFileSize = 'MAX_FILE_SIZE', + | 'MAX_FILE_SIZE' /** Max ref updates */ - MaxRefUpdates = 'MAX_REF_UPDATES', + | 'MAX_REF_UPDATES' /** Merges must be performed via a merge queue. */ - MergeQueue = 'MERGE_QUEUE', + | 'MERGE_QUEUE' /** Merge queue locked ref */ - MergeQueueLockedRef = 'MERGE_QUEUE_LOCKED_REF', + | 'MERGE_QUEUE_LOCKED_REF' /** Prevent users with push access from force pushing to refs. */ - NonFastForward = 'NON_FAST_FORWARD', + | 'NON_FAST_FORWARD' /** Require all commits be made to a non-target branch and submitted via a pull request before they can be merged. */ - PullRequest = 'PULL_REQUEST', + | 'PULL_REQUEST' /** Choose which environments must be successfully deployed to before refs can be pushed into a ref that matches this rule. */ - RequiredDeployments = 'REQUIRED_DEPLOYMENTS', + | 'REQUIRED_DEPLOYMENTS' /** Prevent merge commits from being pushed to matching refs. */ - RequiredLinearHistory = 'REQUIRED_LINEAR_HISTORY', + | 'REQUIRED_LINEAR_HISTORY' /** When enabled, all conversations on code must be resolved before a pull request can be merged into a branch that matches this rule. */ - RequiredReviewThreadResolution = 'REQUIRED_REVIEW_THREAD_RESOLUTION', + | 'REQUIRED_REVIEW_THREAD_RESOLUTION' /** Commits pushed to matching refs must have verified signatures. */ - RequiredSignatures = 'REQUIRED_SIGNATURES', + | 'REQUIRED_SIGNATURES' /** Choose which status checks must pass before the ref is updated. When enabled, commits must first be pushed to another ref where the checks pass. */ - RequiredStatusChecks = 'REQUIRED_STATUS_CHECKS', + | 'REQUIRED_STATUS_CHECKS' /** Require all commits be made to a non-target branch and submitted via a pull request and required workflow checks to pass before they can be merged. */ - RequiredWorkflowStatusChecks = 'REQUIRED_WORKFLOW_STATUS_CHECKS', + | 'REQUIRED_WORKFLOW_STATUS_CHECKS' /** Secret scanning */ - SecretScanning = 'SECRET_SCANNING', + | 'SECRET_SCANNING' /** Tag */ - Tag = 'TAG', + | 'TAG' /** Tag name pattern */ - TagNamePattern = 'TAG_NAME_PATTERN', + | 'TAG_NAME_PATTERN' /** Only allow users with bypass permission to update matching refs. */ - Update = 'UPDATE', + | 'UPDATE' /** Require all changes made to a targeted branch to pass the specified workflows before they can be merged. */ - Workflows = 'WORKFLOWS', + | 'WORKFLOWS' /** Workflow files cannot be modified. */ - WorkflowUpdates = 'WORKFLOW_UPDATES' -} + | 'WORKFLOW_UPDATES'; /** A repository ruleset. */ export type RepositoryRuleset = Node & { @@ -28085,14 +27884,13 @@ export type RepositoryRulesetBypassActor = Node & { }; /** The bypass mode for a specific actor on a ruleset. */ -export enum RepositoryRulesetBypassActorBypassMode { +export type RepositoryRulesetBypassActorBypassMode = /** The actor can always bypass rules */ - Always = 'ALWAYS', + | 'ALWAYS' /** The actor is exempt from rules without generating a pass / fail result */ - Exempt = 'EXEMPT', + | 'EXEMPT' /** The actor can only bypass rules via a pull request */ - PullRequest = 'PULL_REQUEST' -} + | 'PULL_REQUEST'; /** The connection type for RepositoryRulesetBypassActor. */ export type RepositoryRulesetBypassActorConnection = { @@ -28155,24 +27953,22 @@ export type RepositoryRulesetEdge = { }; /** The targets supported for rulesets. */ -export enum RepositoryRulesetTarget { +export type RepositoryRulesetTarget = /** Branch */ - Branch = 'BRANCH', + | 'BRANCH' /** Push */ - Push = 'PUSH', + | 'PUSH' /** repository */ - Repository = 'REPOSITORY', + | 'REPOSITORY' /** Tag */ - Tag = 'TAG' -} + | 'TAG'; /** The possible filters for suggested actors in a repository */ -export enum RepositorySuggestedActorFilter { +export type RepositorySuggestedActorFilter = /** Actors that can be assigned to issues and pull requests */ - CanBeAssigned = 'CAN_BE_ASSIGNED', + | 'CAN_BE_ASSIGNED' /** Actors that can be the author of issues and pull requests */ - CanBeAuthor = 'CAN_BE_AUTHOR' -} + | 'CAN_BE_AUTHOR'; /** A repository-topic connects a repository to a topic. */ export type RepositoryTopic = Node & UniformResourceLocatable & { @@ -28210,14 +28006,13 @@ export type RepositoryTopicEdge = { }; /** The repository's visibility level. */ -export enum RepositoryVisibility { +export type RepositoryVisibility = /** The repository is visible only to users in the same enterprise. */ - Internal = 'INTERNAL', + | 'INTERNAL' /** The repository is visible only to those with explicit access. */ - Private = 'PRIVATE', + | 'PRIVATE' /** The repository is visible to everyone. */ - Public = 'PUBLIC' -} + | 'PUBLIC'; /** Audit log entry for a repository_visibility_change.disable event. */ export type RepositoryVisibilityChangeDisableAuditEntry = AuditEntry & EnterpriseAuditEntryData & Node & OrganizationAuditEntryData & { @@ -28472,24 +28267,22 @@ export type RepositoryVulnerabilityAlertConnection = { }; /** The possible relationships of an alert's dependency. */ -export enum RepositoryVulnerabilityAlertDependencyRelationship { +export type RepositoryVulnerabilityAlertDependencyRelationship = /** A direct dependency of your project */ - Direct = 'DIRECT', + | 'DIRECT' /** The relationship could not be determined */ - Inconclusive = 'INCONCLUSIVE', + | 'INCONCLUSIVE' /** A transitive dependency of your project */ - Transitive = 'TRANSITIVE', + | 'TRANSITIVE' /** The relationship is unknown */ - Unknown = 'UNKNOWN' -} + | 'UNKNOWN'; /** The possible scopes of an alert's dependency. */ -export enum RepositoryVulnerabilityAlertDependencyScope { +export type RepositoryVulnerabilityAlertDependencyScope = /** A dependency that is only used in development */ - Development = 'DEVELOPMENT', + | 'DEVELOPMENT' /** A dependency that is leveraged during application runtime */ - Runtime = 'RUNTIME' -} + | 'RUNTIME'; /** An edge in a connection. */ export type RepositoryVulnerabilityAlertEdge = { @@ -28501,16 +28294,15 @@ export type RepositoryVulnerabilityAlertEdge = { }; /** The possible states of an alert */ -export enum RepositoryVulnerabilityAlertState { +export type RepositoryVulnerabilityAlertState = /** An alert that has been automatically closed by Dependabot. */ - AutoDismissed = 'AUTO_DISMISSED', + | 'AUTO_DISMISSED' /** An alert that has been manually closed by a user. */ - Dismissed = 'DISMISSED', + | 'DISMISSED' /** An alert that has been resolved by a code change. */ - Fixed = 'FIXED', + | 'FIXED' /** An alert that is still open. */ - Open = 'OPEN' -} + | 'OPEN'; /** Autogenerated input type of ReprioritizeSubIssue */ export type ReprioritizeSubIssueInput = { @@ -28565,18 +28357,17 @@ export type RequestReviewsPayload = { }; /** The possible states that can be requested when creating a check run. */ -export enum RequestableCheckStatusState { +export type RequestableCheckStatusState = /** The check suite or run has been completed. */ - Completed = 'COMPLETED', + | 'COMPLETED' /** The check suite or run is in progress. */ - InProgress = 'IN_PROGRESS', + | 'IN_PROGRESS' /** The check suite or run is in pending state. */ - Pending = 'PENDING', + | 'PENDING' /** The check suite or run has been queued. */ - Queued = 'QUEUED', + | 'QUEUED' /** The check suite or run is in waiting state. */ - Waiting = 'WAITING' -} + | 'WAITING'; /** Types that can be requested reviewers. */ export type RequestedReviewer = Bot | Mannequin | Team | User; @@ -28986,24 +28777,22 @@ export type RevokeMigratorRolePayload = { }; /** Possible roles a user may have in relation to an organization. */ -export enum RoleInOrganization { +export type RoleInOrganization = /** A user who is a direct member of the organization. */ - DirectMember = 'DIRECT_MEMBER', + | 'DIRECT_MEMBER' /** A user with full administrative access to the organization. */ - Owner = 'OWNER', + | 'OWNER' /** A user who is unaffiliated with the organization. */ - Unaffiliated = 'UNAFFILIATED' -} + | 'UNAFFILIATED'; /** The level of enforcement for a rule or ruleset. */ -export enum RuleEnforcement { +export type RuleEnforcement = /** Rules will be enforced */ - Active = 'ACTIVE', + | 'ACTIVE' /** Do not evaluate or enforce rules */ - Disabled = 'DISABLED', + | 'DISABLED' /** Allow admins to test rules before enforcing them. Admins can view insights on the Rule Insights page (`evaluate` is only available with GitHub Enterprise). */ - Evaluate = 'EVALUATE' -} + | 'EVALUATE'; /** Types which can be parameters for `RepositoryRule` objects. */ export type RuleParameters = BranchNamePatternParameters | CodeScanningParameters | CommitAuthorEmailPatternParameters | CommitMessagePatternParameters | CommitterEmailPatternParameters | CopilotCodeReviewParameters | FileExtensionRestrictionParameters | FilePathRestrictionParameters | MaxFilePathLengthParameters | MaxFileSizeParameters | MergeQueueParameters | PullRequestParameters | RequiredDeploymentsParameters | RequiredStatusChecksParameters | TagNamePatternParameters | UpdateParameters | WorkflowsParameters; @@ -29050,28 +28839,26 @@ export type RuleParametersInput = { export type RuleSource = Enterprise | Organization | Repository; /** The possible digest algorithms used to sign SAML requests for an identity provider. */ -export enum SamlDigestAlgorithm { +export type SamlDigestAlgorithm = /** SHA1 */ - Sha1 = 'SHA1', + | 'SHA1' /** SHA256 */ - Sha256 = 'SHA256', + | 'SHA256' /** SHA384 */ - Sha384 = 'SHA384', + | 'SHA384' /** SHA512 */ - Sha512 = 'SHA512' -} + | 'SHA512'; /** The possible signature algorithms used to sign SAML requests for a Identity Provider. */ -export enum SamlSignatureAlgorithm { +export type SamlSignatureAlgorithm = /** RSA-SHA1 */ - RsaSha1 = 'RSA_SHA1', + | 'RSA_SHA1' /** RSA-SHA256 */ - RsaSha256 = 'RSA_SHA256', + | 'RSA_SHA256' /** RSA-SHA384 */ - RsaSha384 = 'RSA_SHA384', + | 'RSA_SHA384' /** RSA-SHA512 */ - RsaSha512 = 'RSA_SHA512' -} + | 'RSA_SHA512'; /** A Saved Reply is text a user can use to reply quickly. */ export type SavedReply = Node & { @@ -29121,10 +28908,9 @@ export type SavedReplyOrder = { }; /** Properties by which saved reply connections can be ordered. */ -export enum SavedReplyOrderField { +export type SavedReplyOrderField = /** Order saved reply by when they were updated. */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** The results of a search. */ export type SearchResultItem = App | Discussion | Issue | MarketplaceListing | Organization | PullRequest | Repository | User; @@ -29164,18 +28950,17 @@ export type SearchResultItemEdge = { }; /** Represents the individual results of a search. */ -export enum SearchType { +export type SearchType = /** Returns matching discussions in repositories. */ - Discussion = 'DISCUSSION', + | 'DISCUSSION' /** Returns results matching issues in repositories. */ - Issue = 'ISSUE', + | 'ISSUE' /** Returns results matching issues in repositories. */ - IssueAdvanced = 'ISSUE_ADVANCED', + | 'ISSUE_ADVANCED' /** Returns results matching repositories. */ - Repository = 'REPOSITORY', + | 'REPOSITORY' /** Returns results matching users and organizations on GitHub. */ - User = 'USER' -} + | 'USER'; /** A GitHub Security Advisory */ export type SecurityAdvisory = Node & { @@ -29249,12 +29034,11 @@ export type SecurityAdvisoryVulnerabilitiesArgs = { }; /** Classification of the advisory. */ -export enum SecurityAdvisoryClassification { +export type SecurityAdvisoryClassification = /** Classification of general advisories. */ - General = 'GENERAL', + | 'GENERAL' /** Classification of malware advisories. */ - Malware = 'MALWARE' -} + | 'MALWARE'; /** The connection type for SecurityAdvisory. */ export type SecurityAdvisoryConnection = { @@ -29270,32 +29054,31 @@ export type SecurityAdvisoryConnection = { }; /** The possible ecosystems of a security vulnerability's package. */ -export enum SecurityAdvisoryEcosystem { +export type SecurityAdvisoryEcosystem = /** GitHub Actions */ - Actions = 'ACTIONS', + | 'ACTIONS' /** PHP packages hosted at packagist.org */ - Composer = 'COMPOSER', + | 'COMPOSER' /** Erlang/Elixir packages hosted at hex.pm */ - Erlang = 'ERLANG', + | 'ERLANG' /** Go modules */ - Go = 'GO', + | 'GO' /** Java artifacts hosted at the Maven central repository */ - Maven = 'MAVEN', + | 'MAVEN' /** JavaScript packages hosted at npmjs.com */ - Npm = 'NPM', + | 'NPM' /** .NET packages hosted at the NuGet Gallery */ - Nuget = 'NUGET', + | 'NUGET' /** Python packages hosted at PyPI.org */ - Pip = 'PIP', + | 'PIP' /** Dart packages hosted at pub.dev */ - Pub = 'PUB', + | 'PUB' /** Ruby gems hosted at RubyGems.org */ - Rubygems = 'RUBYGEMS', + | 'RUBYGEMS' /** Rust crates */ - Rust = 'RUST', + | 'RUST' /** Swift packages */ - Swift = 'SWIFT' -} + | 'SWIFT'; /** An edge in a connection. */ export type SecurityAdvisoryEdge = { @@ -29324,12 +29107,11 @@ export type SecurityAdvisoryIdentifierFilter = { }; /** Identifier formats available for advisories. */ -export enum SecurityAdvisoryIdentifierType { +export type SecurityAdvisoryIdentifierType = /** Common Vulnerabilities and Exposures Identifier. */ - Cve = 'CVE', + | 'CVE' /** GitHub Security Advisory ID. */ - Ghsa = 'GHSA' -} + | 'GHSA'; /** Ordering options for security advisory connections */ export type SecurityAdvisoryOrder = { @@ -29340,16 +29122,15 @@ export type SecurityAdvisoryOrder = { }; /** Properties by which security advisory connections can be ordered. */ -export enum SecurityAdvisoryOrderField { +export type SecurityAdvisoryOrderField = /** Order advisories by EPSS percentage */ - EpssPercentage = 'EPSS_PERCENTAGE', + | 'EPSS_PERCENTAGE' /** Order advisories by EPSS percentile */ - EpssPercentile = 'EPSS_PERCENTILE', + | 'EPSS_PERCENTILE' /** Order advisories by publication time */ - PublishedAt = 'PUBLISHED_AT', + | 'PUBLISHED_AT' /** Order advisories by update time */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** An individual package */ export type SecurityAdvisoryPackage = { @@ -29375,16 +29156,15 @@ export type SecurityAdvisoryReference = { }; /** Severity of the vulnerability. */ -export enum SecurityAdvisorySeverity { +export type SecurityAdvisorySeverity = /** Critical. */ - Critical = 'CRITICAL', + | 'CRITICAL' /** High. */ - High = 'HIGH', + | 'HIGH' /** Low. */ - Low = 'LOW', + | 'LOW' /** Moderate. */ - Moderate = 'MODERATE' -} + | 'MODERATE'; /** An individual vulnerability within an Advisory */ export type SecurityVulnerability = { @@ -29443,10 +29223,9 @@ export type SecurityVulnerabilityOrder = { }; /** Properties by which security vulnerability connections can be ordered. */ -export enum SecurityVulnerabilityOrderField { +export type SecurityVulnerabilityOrderField = /** Order vulnerability by update time */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** Autogenerated input type of SetEnterpriseIdentityProvider */ export type SetEnterpriseIdentityProviderInput = { @@ -29612,32 +29391,31 @@ export type SocialAccountEdge = { }; /** Software or company that hosts social media accounts. */ -export enum SocialAccountProvider { +export type SocialAccountProvider = /** Decentralized microblogging social platform. */ - Bluesky = 'BLUESKY', + | 'BLUESKY' /** Social media and networking website. */ - Facebook = 'FACEBOOK', + | 'FACEBOOK' /** Catch-all for social media providers that do not yet have specific handling. */ - Generic = 'GENERIC', + | 'GENERIC' /** Fork of Mastodon with a greater focus on local posting. */ - Hometown = 'HOMETOWN', + | 'HOMETOWN' /** Social media website with a focus on photo and video sharing. */ - Instagram = 'INSTAGRAM', + | 'INSTAGRAM' /** Professional networking website. */ - Linkedin = 'LINKEDIN', + | 'LINKEDIN' /** Open-source federated microblogging service. */ - Mastodon = 'MASTODON', + | 'MASTODON' /** JavaScript package registry. */ - Npm = 'NPM', + | 'NPM' /** Social news aggregation and discussion website. */ - Reddit = 'REDDIT', + | 'REDDIT' /** Live-streaming service. */ - Twitch = 'TWITCH', + | 'TWITCH' /** Microblogging website. */ - Twitter = 'TWITTER', + | 'TWITTER' /** Online video platform. */ - Youtube = 'YOUTUBE' -} + | 'YOUTUBE'; /** Entities that can sponsor others via GitHub Sponsors */ export type Sponsor = Organization | User; @@ -29686,14 +29464,13 @@ export type SponsorAndLifetimeValueOrder = { }; /** Properties by which sponsor and lifetime value connections can be ordered. */ -export enum SponsorAndLifetimeValueOrderField { +export type SponsorAndLifetimeValueOrderField = /** Order results by how much money the sponsor has paid in total. */ - LifetimeValue = 'LIFETIME_VALUE', + | 'LIFETIME_VALUE' /** Order results by the sponsor's login (username). */ - SponsorLogin = 'SPONSOR_LOGIN', + | 'SPONSOR_LOGIN' /** Order results by the sponsor's relevance to the viewer. */ - SponsorRelevance = 'SPONSOR_RELEVANCE' -} + | 'SPONSOR_RELEVANCE'; /** A list of users and organizations sponsoring someone via GitHub Sponsors. */ export type SponsorConnection = { @@ -29726,12 +29503,11 @@ export type SponsorOrder = { }; /** Properties by which sponsor connections can be ordered. */ -export enum SponsorOrderField { +export type SponsorOrderField = /** Order sponsorable entities by login (username). */ - Login = 'LOGIN', + | 'LOGIN' /** Order sponsors by their relevance to the viewer. */ - Relevance = 'RELEVANCE' -} + | 'RELEVANCE'; /** Entities that can sponsor or be sponsored through GitHub Sponsors. */ export type Sponsorable = { @@ -29914,10 +29690,9 @@ export type SponsorableOrder = { }; /** Properties by which sponsorable connections can be ordered. */ -export enum SponsorableOrderField { +export type SponsorableOrderField = /** Order sponsorable entities by login (username). */ - Login = 'LOGIN' -} + | 'LOGIN'; /** An event related to sponsorship activity. */ export type SponsorsActivity = Node & { @@ -29945,20 +29720,19 @@ export type SponsorsActivity = Node & { }; /** The possible actions that GitHub Sponsors activities can represent. */ -export enum SponsorsActivityAction { +export type SponsorsActivityAction = /** The activity was cancelling a sponsorship. */ - CancelledSponsorship = 'CANCELLED_SPONSORSHIP', + | 'CANCELLED_SPONSORSHIP' /** The activity was starting a sponsorship. */ - NewSponsorship = 'NEW_SPONSORSHIP', + | 'NEW_SPONSORSHIP' /** The activity was scheduling a downgrade or cancellation. */ - PendingChange = 'PENDING_CHANGE', + | 'PENDING_CHANGE' /** The activity was funds being refunded to the sponsor or GitHub. */ - Refund = 'REFUND', + | 'REFUND' /** The activity was disabling matching for a previously matched sponsorship. */ - SponsorMatchDisabled = 'SPONSOR_MATCH_DISABLED', + | 'SPONSOR_MATCH_DISABLED' /** The activity was changing the sponsorship tier, either directly by the sponsor or by a scheduled/pending change. */ - TierChange = 'TIER_CHANGE' -} + | 'TIER_CHANGE'; /** The connection type for SponsorsActivity. */ export type SponsorsActivityConnection = { @@ -29991,520 +29765,517 @@ export type SponsorsActivityOrder = { }; /** Properties by which GitHub Sponsors activity connections can be ordered. */ -export enum SponsorsActivityOrderField { +export type SponsorsActivityOrderField = /** Order activities by when they happened. */ - Timestamp = 'TIMESTAMP' -} + | 'TIMESTAMP'; /** The possible time periods for which Sponsors activities can be requested. */ -export enum SponsorsActivityPeriod { +export type SponsorsActivityPeriod = /** Don't restrict the activity to any date range, include all activity. */ - All = 'ALL', + | 'ALL' /** The previous calendar day. */ - Day = 'DAY', + | 'DAY' /** The previous thirty days. */ - Month = 'MONTH', + | 'MONTH' /** The previous seven days. */ - Week = 'WEEK' -} + | 'WEEK'; /** Represents countries or regions for billing and residence for a GitHub Sponsors profile. */ -export enum SponsorsCountryOrRegionCode { +export type SponsorsCountryOrRegionCode = /** Andorra */ - Ad = 'AD', + | 'AD' /** United Arab Emirates */ - Ae = 'AE', + | 'AE' /** Afghanistan */ - Af = 'AF', + | 'AF' /** Antigua and Barbuda */ - Ag = 'AG', + | 'AG' /** Anguilla */ - Ai = 'AI', + | 'AI' /** Albania */ - Al = 'AL', + | 'AL' /** Armenia */ - Am = 'AM', + | 'AM' /** Angola */ - Ao = 'AO', + | 'AO' /** Antarctica */ - Aq = 'AQ', + | 'AQ' /** Argentina */ - Ar = 'AR', + | 'AR' /** American Samoa */ - As = 'AS', + | 'AS' /** Austria */ - At = 'AT', + | 'AT' /** Australia */ - Au = 'AU', + | 'AU' /** Aruba */ - Aw = 'AW', + | 'AW' /** Åland */ - Ax = 'AX', + | 'AX' /** Azerbaijan */ - Az = 'AZ', + | 'AZ' /** Bosnia and Herzegovina */ - Ba = 'BA', + | 'BA' /** Barbados */ - Bb = 'BB', + | 'BB' /** Bangladesh */ - Bd = 'BD', + | 'BD' /** Belgium */ - Be = 'BE', + | 'BE' /** Burkina Faso */ - Bf = 'BF', + | 'BF' /** Bulgaria */ - Bg = 'BG', + | 'BG' /** Bahrain */ - Bh = 'BH', + | 'BH' /** Burundi */ - Bi = 'BI', + | 'BI' /** Benin */ - Bj = 'BJ', + | 'BJ' /** Saint Barthélemy */ - Bl = 'BL', + | 'BL' /** Bermuda */ - Bm = 'BM', + | 'BM' /** Brunei Darussalam */ - Bn = 'BN', + | 'BN' /** Bolivia */ - Bo = 'BO', + | 'BO' /** Bonaire, Sint Eustatius and Saba */ - Bq = 'BQ', + | 'BQ' /** Brazil */ - Br = 'BR', + | 'BR' /** Bahamas */ - Bs = 'BS', + | 'BS' /** Bhutan */ - Bt = 'BT', + | 'BT' /** Bouvet Island */ - Bv = 'BV', + | 'BV' /** Botswana */ - Bw = 'BW', + | 'BW' /** Belarus */ - By = 'BY', + | 'BY' /** Belize */ - Bz = 'BZ', + | 'BZ' /** Canada */ - Ca = 'CA', + | 'CA' /** Cocos (Keeling) Islands */ - Cc = 'CC', + | 'CC' /** Congo (Kinshasa) */ - Cd = 'CD', + | 'CD' /** Central African Republic */ - Cf = 'CF', + | 'CF' /** Congo (Brazzaville) */ - Cg = 'CG', + | 'CG' /** Switzerland */ - Ch = 'CH', + | 'CH' /** Côte d'Ivoire */ - Ci = 'CI', + | 'CI' /** Cook Islands */ - Ck = 'CK', + | 'CK' /** Chile */ - Cl = 'CL', + | 'CL' /** Cameroon */ - Cm = 'CM', + | 'CM' /** China */ - Cn = 'CN', + | 'CN' /** Colombia */ - Co = 'CO', + | 'CO' /** Costa Rica */ - Cr = 'CR', + | 'CR' /** Cape Verde */ - Cv = 'CV', + | 'CV' /** Curaçao */ - Cw = 'CW', + | 'CW' /** Christmas Island */ - Cx = 'CX', + | 'CX' /** Cyprus */ - Cy = 'CY', + | 'CY' /** Czech Republic */ - Cz = 'CZ', + | 'CZ' /** Germany */ - De = 'DE', + | 'DE' /** Djibouti */ - Dj = 'DJ', + | 'DJ' /** Denmark */ - Dk = 'DK', + | 'DK' /** Dominica */ - Dm = 'DM', + | 'DM' /** Dominican Republic */ - Do = 'DO', + | 'DO' /** Algeria */ - Dz = 'DZ', + | 'DZ' /** Ecuador */ - Ec = 'EC', + | 'EC' /** Estonia */ - Ee = 'EE', + | 'EE' /** Egypt */ - Eg = 'EG', + | 'EG' /** Western Sahara */ - Eh = 'EH', + | 'EH' /** Eritrea */ - Er = 'ER', + | 'ER' /** Spain */ - Es = 'ES', + | 'ES' /** Ethiopia */ - Et = 'ET', + | 'ET' /** Finland */ - Fi = 'FI', + | 'FI' /** Fiji */ - Fj = 'FJ', + | 'FJ' /** Falkland Islands */ - Fk = 'FK', + | 'FK' /** Micronesia */ - Fm = 'FM', + | 'FM' /** Faroe Islands */ - Fo = 'FO', + | 'FO' /** France */ - Fr = 'FR', + | 'FR' /** Gabon */ - Ga = 'GA', + | 'GA' /** United Kingdom */ - Gb = 'GB', + | 'GB' /** Grenada */ - Gd = 'GD', + | 'GD' /** Georgia */ - Ge = 'GE', + | 'GE' /** French Guiana */ - Gf = 'GF', + | 'GF' /** Guernsey */ - Gg = 'GG', + | 'GG' /** Ghana */ - Gh = 'GH', + | 'GH' /** Gibraltar */ - Gi = 'GI', + | 'GI' /** Greenland */ - Gl = 'GL', + | 'GL' /** Gambia */ - Gm = 'GM', + | 'GM' /** Guinea */ - Gn = 'GN', + | 'GN' /** Guadeloupe */ - Gp = 'GP', + | 'GP' /** Equatorial Guinea */ - Gq = 'GQ', + | 'GQ' /** Greece */ - Gr = 'GR', + | 'GR' /** South Georgia and South Sandwich Islands */ - Gs = 'GS', + | 'GS' /** Guatemala */ - Gt = 'GT', + | 'GT' /** Guam */ - Gu = 'GU', + | 'GU' /** Guinea-Bissau */ - Gw = 'GW', + | 'GW' /** Guyana */ - Gy = 'GY', + | 'GY' /** Hong Kong */ - Hk = 'HK', + | 'HK' /** Heard and McDonald Islands */ - Hm = 'HM', + | 'HM' /** Honduras */ - Hn = 'HN', + | 'HN' /** Croatia */ - Hr = 'HR', + | 'HR' /** Haiti */ - Ht = 'HT', + | 'HT' /** Hungary */ - Hu = 'HU', + | 'HU' /** Indonesia */ - Id = 'ID', + | 'ID' /** Ireland */ - Ie = 'IE', + | 'IE' /** Israel */ - Il = 'IL', + | 'IL' /** Isle of Man */ - Im = 'IM', + | 'IM' /** India */ - In = 'IN', + | 'IN' /** British Indian Ocean Territory */ - Io = 'IO', + | 'IO' /** Iraq */ - Iq = 'IQ', + | 'IQ' /** Iran */ - Ir = 'IR', + | 'IR' /** Iceland */ - Is = 'IS', + | 'IS' /** Italy */ - It = 'IT', + | 'IT' /** Jersey */ - Je = 'JE', + | 'JE' /** Jamaica */ - Jm = 'JM', + | 'JM' /** Jordan */ - Jo = 'JO', + | 'JO' /** Japan */ - Jp = 'JP', + | 'JP' /** Kenya */ - Ke = 'KE', + | 'KE' /** Kyrgyzstan */ - Kg = 'KG', + | 'KG' /** Cambodia */ - Kh = 'KH', + | 'KH' /** Kiribati */ - Ki = 'KI', + | 'KI' /** Comoros */ - Km = 'KM', + | 'KM' /** Saint Kitts and Nevis */ - Kn = 'KN', + | 'KN' /** Korea, South */ - Kr = 'KR', + | 'KR' /** Kuwait */ - Kw = 'KW', + | 'KW' /** Cayman Islands */ - Ky = 'KY', + | 'KY' /** Kazakhstan */ - Kz = 'KZ', + | 'KZ' /** Laos */ - La = 'LA', + | 'LA' /** Lebanon */ - Lb = 'LB', + | 'LB' /** Saint Lucia */ - Lc = 'LC', + | 'LC' /** Liechtenstein */ - Li = 'LI', + | 'LI' /** Sri Lanka */ - Lk = 'LK', + | 'LK' /** Liberia */ - Lr = 'LR', + | 'LR' /** Lesotho */ - Ls = 'LS', + | 'LS' /** Lithuania */ - Lt = 'LT', + | 'LT' /** Luxembourg */ - Lu = 'LU', + | 'LU' /** Latvia */ - Lv = 'LV', + | 'LV' /** Libya */ - Ly = 'LY', + | 'LY' /** Morocco */ - Ma = 'MA', + | 'MA' /** Monaco */ - Mc = 'MC', + | 'MC' /** Moldova */ - Md = 'MD', + | 'MD' /** Montenegro */ - Me = 'ME', + | 'ME' /** Saint Martin (French part) */ - Mf = 'MF', + | 'MF' /** Madagascar */ - Mg = 'MG', + | 'MG' /** Marshall Islands */ - Mh = 'MH', + | 'MH' /** Macedonia */ - Mk = 'MK', + | 'MK' /** Mali */ - Ml = 'ML', + | 'ML' /** Myanmar */ - Mm = 'MM', + | 'MM' /** Mongolia */ - Mn = 'MN', + | 'MN' /** Macau */ - Mo = 'MO', + | 'MO' /** Northern Mariana Islands */ - Mp = 'MP', + | 'MP' /** Martinique */ - Mq = 'MQ', + | 'MQ' /** Mauritania */ - Mr = 'MR', + | 'MR' /** Montserrat */ - Ms = 'MS', + | 'MS' /** Malta */ - Mt = 'MT', + | 'MT' /** Mauritius */ - Mu = 'MU', + | 'MU' /** Maldives */ - Mv = 'MV', + | 'MV' /** Malawi */ - Mw = 'MW', + | 'MW' /** Mexico */ - Mx = 'MX', + | 'MX' /** Malaysia */ - My = 'MY', + | 'MY' /** Mozambique */ - Mz = 'MZ', + | 'MZ' /** Namibia */ - Na = 'NA', + | 'NA' /** New Caledonia */ - Nc = 'NC', + | 'NC' /** Niger */ - Ne = 'NE', + | 'NE' /** Norfolk Island */ - Nf = 'NF', + | 'NF' /** Nigeria */ - Ng = 'NG', + | 'NG' /** Nicaragua */ - Ni = 'NI', + | 'NI' /** Netherlands */ - Nl = 'NL', + | 'NL' /** Norway */ - No = 'NO', + | 'NO' /** Nepal */ - Np = 'NP', + | 'NP' /** Nauru */ - Nr = 'NR', + | 'NR' /** Niue */ - Nu = 'NU', + | 'NU' /** New Zealand */ - Nz = 'NZ', + | 'NZ' /** Oman */ - Om = 'OM', + | 'OM' /** Panama */ - Pa = 'PA', + | 'PA' /** Peru */ - Pe = 'PE', + | 'PE' /** French Polynesia */ - Pf = 'PF', + | 'PF' /** Papua New Guinea */ - Pg = 'PG', + | 'PG' /** Philippines */ - Ph = 'PH', + | 'PH' /** Pakistan */ - Pk = 'PK', + | 'PK' /** Poland */ - Pl = 'PL', + | 'PL' /** Saint Pierre and Miquelon */ - Pm = 'PM', + | 'PM' /** Pitcairn */ - Pn = 'PN', + | 'PN' /** Puerto Rico */ - Pr = 'PR', + | 'PR' /** Palestine */ - Ps = 'PS', + | 'PS' /** Portugal */ - Pt = 'PT', + | 'PT' /** Palau */ - Pw = 'PW', + | 'PW' /** Paraguay */ - Py = 'PY', + | 'PY' /** Qatar */ - Qa = 'QA', + | 'QA' /** Reunion */ - Re = 'RE', + | 'RE' /** Romania */ - Ro = 'RO', + | 'RO' /** Serbia */ - Rs = 'RS', + | 'RS' /** Russian Federation */ - Ru = 'RU', + | 'RU' /** Rwanda */ - Rw = 'RW', + | 'RW' /** Saudi Arabia */ - Sa = 'SA', + | 'SA' /** Solomon Islands */ - Sb = 'SB', + | 'SB' /** Seychelles */ - Sc = 'SC', + | 'SC' /** Sudan */ - Sd = 'SD', + | 'SD' /** Sweden */ - Se = 'SE', + | 'SE' /** Singapore */ - Sg = 'SG', + | 'SG' /** Saint Helena */ - Sh = 'SH', + | 'SH' /** Slovenia */ - Si = 'SI', + | 'SI' /** Svalbard and Jan Mayen Islands */ - Sj = 'SJ', + | 'SJ' /** Slovakia */ - Sk = 'SK', + | 'SK' /** Sierra Leone */ - Sl = 'SL', + | 'SL' /** San Marino */ - Sm = 'SM', + | 'SM' /** Senegal */ - Sn = 'SN', + | 'SN' /** Somalia */ - So = 'SO', + | 'SO' /** Suriname */ - Sr = 'SR', + | 'SR' /** South Sudan */ - Ss = 'SS', + | 'SS' /** Sao Tome and Principe */ - St = 'ST', + | 'ST' /** El Salvador */ - Sv = 'SV', + | 'SV' /** Sint Maarten (Dutch part) */ - Sx = 'SX', + | 'SX' /** Syria */ - Sy = 'SY', + | 'SY' /** Swaziland */ - Sz = 'SZ', + | 'SZ' /** Turks and Caicos Islands */ - Tc = 'TC', + | 'TC' /** Chad */ - Td = 'TD', + | 'TD' /** French Southern Lands */ - Tf = 'TF', + | 'TF' /** Togo */ - Tg = 'TG', + | 'TG' /** Thailand */ - Th = 'TH', + | 'TH' /** Tajikistan */ - Tj = 'TJ', + | 'TJ' /** Tokelau */ - Tk = 'TK', + | 'TK' /** Timor-Leste */ - Tl = 'TL', + | 'TL' /** Turkmenistan */ - Tm = 'TM', + | 'TM' /** Tunisia */ - Tn = 'TN', + | 'TN' /** Tonga */ - To = 'TO', + | 'TO' /** Türkiye */ - Tr = 'TR', + | 'TR' /** Trinidad and Tobago */ - Tt = 'TT', + | 'TT' /** Tuvalu */ - Tv = 'TV', + | 'TV' /** Taiwan */ - Tw = 'TW', + | 'TW' /** Tanzania */ - Tz = 'TZ', + | 'TZ' /** Ukraine */ - Ua = 'UA', + | 'UA' /** Uganda */ - Ug = 'UG', + | 'UG' /** United States Minor Outlying Islands */ - Um = 'UM', + | 'UM' /** United States of America */ - Us = 'US', + | 'US' /** Uruguay */ - Uy = 'UY', + | 'UY' /** Uzbekistan */ - Uz = 'UZ', + | 'UZ' /** Vatican City */ - Va = 'VA', + | 'VA' /** Saint Vincent and the Grenadines */ - Vc = 'VC', + | 'VC' /** Venezuela */ - Ve = 'VE', + | 'VE' /** Virgin Islands, British */ - Vg = 'VG', + | 'VG' /** Virgin Islands, U.S. */ - Vi = 'VI', + | 'VI' /** Vietnam */ - Vn = 'VN', + | 'VN' /** Vanuatu */ - Vu = 'VU', + | 'VU' /** Wallis and Futuna Islands */ - Wf = 'WF', + | 'WF' /** Samoa */ - Ws = 'WS', + | 'WS' /** Yemen */ - Ye = 'YE', + | 'YE' /** Mayotte */ - Yt = 'YT', + | 'YT' /** South Africa */ - Za = 'ZA', + | 'ZA' /** Zambia */ - Zm = 'ZM', + | 'ZM' /** Zimbabwe */ - Zw = 'ZW' -} + | 'ZW'; /** A goal associated with a GitHub Sponsors listing, representing a target the sponsored maintainer would like to attain. */ export type SponsorsGoal = { @@ -30522,12 +30293,11 @@ export type SponsorsGoal = { }; /** The different kinds of goals a GitHub Sponsors member can have. */ -export enum SponsorsGoalKind { +export type SponsorsGoalKind = /** The goal is about getting a certain amount in USD from sponsorships each month. */ - MonthlySponsorshipAmount = 'MONTHLY_SPONSORSHIP_AMOUNT', + | 'MONTHLY_SPONSORSHIP_AMOUNT' /** The goal is about reaching a certain number of sponsors. */ - TotalSponsorsCount = 'TOTAL_SPONSORS_COUNT' -} + | 'TOTAL_SPONSORS_COUNT'; /** A GitHub Sponsors listing. */ export type SponsorsListing = Node & { @@ -30618,12 +30388,11 @@ export type SponsorsListingFeaturedItem = Node & { }; /** The different kinds of records that can be featured on a GitHub Sponsors profile page. */ -export enum SponsorsListingFeaturedItemFeatureableType { +export type SponsorsListingFeaturedItemFeatureableType = /** A repository owned by the user or organization with the GitHub Sponsors profile. */ - Repository = 'REPOSITORY', + | 'REPOSITORY' /** A user who belongs to the organization with the GitHub Sponsors profile. */ - User = 'USER' -} + | 'USER'; /** A GitHub Sponsors tier associated with a GitHub Sponsors listing. */ export type SponsorsTier = Node & { @@ -30711,12 +30480,11 @@ export type SponsorsTierOrder = { }; /** Properties by which Sponsors tiers connections can be ordered. */ -export enum SponsorsTierOrderField { +export type SponsorsTierOrderField = /** Order tiers by creation time. */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order tiers by their monthly price in cents */ - MonthlyPriceInCents = 'MONTHLY_PRICE_IN_CENTS' -} + | 'MONTHLY_PRICE_IN_CENTS'; /** A sponsorship relationship between a sponsor and a maintainer */ export type Sponsorship = Node & { @@ -30833,10 +30601,9 @@ export type SponsorshipNewsletterOrder = { }; /** Properties by which sponsorship update connections can be ordered. */ -export enum SponsorshipNewsletterOrderField { +export type SponsorshipNewsletterOrderField = /** Order sponsorship newsletters by when they were created. */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** Ordering options for sponsorship connections. */ export type SponsorshipOrder = { @@ -30847,44 +30614,39 @@ export type SponsorshipOrder = { }; /** Properties by which sponsorship connections can be ordered. */ -export enum SponsorshipOrderField { +export type SponsorshipOrderField = /** Order sponsorship by creation time. */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** How payment was made for funding a GitHub Sponsors sponsorship. */ -export enum SponsorshipPaymentSource { +export type SponsorshipPaymentSource = /** Payment was made through GitHub. */ - Github = 'GITHUB', + | 'GITHUB' /** Payment was made through Patreon. */ - Patreon = 'PATREON' -} + | 'PATREON'; /** The privacy of a sponsorship */ -export enum SponsorshipPrivacy { +export type SponsorshipPrivacy = /** Private */ - Private = 'PRIVATE', + | 'PRIVATE' /** Public */ - Public = 'PUBLIC' -} + | 'PUBLIC'; /** The possible default commit messages for squash merges. */ -export enum SquashMergeCommitMessage { +export type SquashMergeCommitMessage = /** Default to a blank commit message. */ - Blank = 'BLANK', + | 'BLANK' /** Default to the branch's commit messages. */ - CommitMessages = 'COMMIT_MESSAGES', + | 'COMMIT_MESSAGES' /** Default to the pull request's body. */ - PrBody = 'PR_BODY' -} + | 'PR_BODY'; /** The possible default commit titles for squash merges. */ -export enum SquashMergeCommitTitle { +export type SquashMergeCommitTitle = /** Default to the commit's title (if only one commit) or the pull request's title (when more than one commit). */ - CommitOrPrTitle = 'COMMIT_OR_PR_TITLE', + | 'COMMIT_OR_PR_TITLE' /** Default to the pull request's title. */ - PrTitle = 'PR_TITLE' -} + | 'PR_TITLE'; /** Represents an SSH signature on a Commit or Tag. */ export type SshSignature = GitSignature & { @@ -30918,10 +30680,9 @@ export type StarOrder = { }; /** Properties by which star connections can be ordered. */ -export enum StarOrderField { +export type StarOrderField = /** Allows ordering a list of stars by when they were created. */ - StarredAt = 'STARRED_AT' -} + | 'STARRED_AT'; /** The connection type for User. */ export type StargazerConnection = { @@ -31210,18 +30971,17 @@ export type StatusContextStateCount = { }; /** The possible commit status states. */ -export enum StatusState { +export type StatusState = /** Status is errored. */ - Error = 'ERROR', + | 'ERROR' /** Status is expected. */ - Expected = 'EXPECTED', + | 'EXPECTED' /** Status is failing. */ - Failure = 'FAILURE', + | 'FAILURE' /** Status is pending. */ - Pending = 'PENDING', + | 'PENDING' /** Status is successful. */ - Success = 'SUCCESS' -} + | 'SUCCESS'; /** A Stripe Connect account for receiving sponsorship funds from GitHub Sponsors. */ export type StripeConnectAccount = { @@ -31375,14 +31135,13 @@ export type SubscribedEvent = Node & { }; /** The possible states of a subscription. */ -export enum SubscriptionState { +export type SubscriptionState = /** The User is never notified. */ - Ignored = 'IGNORED', + | 'IGNORED' /** The User is notified of all conversations. */ - Subscribed = 'SUBSCRIBED', + | 'SUBSCRIBED' /** The User is only notified when participating or @mentioned. */ - Unsubscribed = 'UNSUBSCRIBED' -} + | 'UNSUBSCRIBED'; /** A suggestion to review a pull request based on a user's commit history and review comments. */ export type SuggestedReviewer = { @@ -32227,10 +31986,9 @@ export type TeamDiscussionOrder = { }; /** Properties by which team discussion connections can be ordered. */ -export enum TeamDiscussionOrderField { +export type TeamDiscussionOrderField = /** Allows chronological ordering of team discussions. */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** An edge in a connection. */ export type TeamEdge = { @@ -32277,38 +32035,34 @@ export type TeamMemberOrder = { }; /** Properties by which team member connections can be ordered. */ -export enum TeamMemberOrderField { +export type TeamMemberOrderField = /** Order team members by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order team members by login */ - Login = 'LOGIN' -} + | 'LOGIN'; /** The possible team member roles; either 'maintainer' or 'member'. */ -export enum TeamMemberRole { +export type TeamMemberRole = /** A team maintainer has permission to add and remove team members. */ - Maintainer = 'MAINTAINER', + | 'MAINTAINER' /** A team member has no administrative permissions on the team. */ - Member = 'MEMBER' -} + | 'MEMBER'; /** Defines which types of team members are included in the returned list. Can be one of IMMEDIATE, CHILD_TEAM or ALL. */ -export enum TeamMembershipType { +export type TeamMembershipType = /** Includes immediate and child team members for the team. */ - All = 'ALL', + | 'ALL' /** Includes only child team members for the team. */ - ChildTeam = 'CHILD_TEAM', + | 'CHILD_TEAM' /** Includes only immediate members of the team. */ - Immediate = 'IMMEDIATE' -} + | 'IMMEDIATE'; /** The possible team notification values. */ -export enum TeamNotificationSetting { +export type TeamNotificationSetting = /** No one will receive notifications. */ - NotificationsDisabled = 'NOTIFICATIONS_DISABLED', + | 'NOTIFICATIONS_DISABLED' /** Everyone will receive notifications when the team is @mentioned. */ - NotificationsEnabled = 'NOTIFICATIONS_ENABLED' -} + | 'NOTIFICATIONS_ENABLED'; /** Ways in which team connections can be ordered. */ export type TeamOrder = { @@ -32319,18 +32073,16 @@ export type TeamOrder = { }; /** Properties by which team connections can be ordered. */ -export enum TeamOrderField { +export type TeamOrderField = /** Allows ordering a list of teams by name. */ - Name = 'NAME' -} + | 'NAME'; /** The possible team privacy values. */ -export enum TeamPrivacy { +export type TeamPrivacy = /** A secret team can only be seen by its members. */ - Secret = 'SECRET', + | 'SECRET' /** A visible team can be seen and @mentioned by every member of the organization. */ - Visible = 'VISIBLE' -} + | 'VISIBLE'; /** Audit log entry for a team.remove_member event. */ export type TeamRemoveMemberAuditEntry = AuditEntry & Node & OrganizationAuditEntryData & TeamAuditEntryData & { @@ -32582,36 +32334,33 @@ export type TeamRepositoryOrder = { }; /** Properties by which team repository connections can be ordered. */ -export enum TeamRepositoryOrderField { +export type TeamRepositoryOrderField = /** Order repositories by creation time */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order repositories by name */ - Name = 'NAME', + | 'NAME' /** Order repositories by permission */ - Permission = 'PERMISSION', + | 'PERMISSION' /** Order repositories by push time */ - PushedAt = 'PUSHED_AT', + | 'PUSHED_AT' /** Order repositories by number of stargazers */ - Stargazers = 'STARGAZERS', + | 'STARGAZERS' /** Order repositories by update time */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** The possible team review assignment algorithms */ -export enum TeamReviewAssignmentAlgorithm { +export type TeamReviewAssignmentAlgorithm = /** Balance review load across the entire team */ - LoadBalance = 'LOAD_BALANCE', + | 'LOAD_BALANCE' /** Alternate reviews between each team member */ - RoundRobin = 'ROUND_ROBIN' -} + | 'ROUND_ROBIN'; /** The role of a user on a team. */ -export enum TeamRole { +export type TeamRole = /** User has admin rights on the team. */ - Admin = 'ADMIN', + | 'ADMIN' /** User is a member of the team. */ - Member = 'MEMBER' -} + | 'MEMBER'; /** A text match within a search result. */ export type TextMatch = { @@ -32636,36 +32385,34 @@ export type TextMatchHighlight = { }; /** The possible states of a thread subscription form action */ -export enum ThreadSubscriptionFormAction { +export type ThreadSubscriptionFormAction = /** The User cannot subscribe or unsubscribe to the thread */ - None = 'NONE', + | 'NONE' /** The User can subscribe to the thread */ - Subscribe = 'SUBSCRIBE', + | 'SUBSCRIBE' /** The User can unsubscribe to the thread */ - Unsubscribe = 'UNSUBSCRIBE' -} + | 'UNSUBSCRIBE'; /** The possible states of a subscription. */ -export enum ThreadSubscriptionState { +export type ThreadSubscriptionState = /** The subscription status is currently disabled. */ - Disabled = 'DISABLED', + | 'DISABLED' /** The User is never notified because they are ignoring the list */ - IgnoringList = 'IGNORING_LIST', + | 'IGNORING_LIST' /** The User is never notified because they are ignoring the thread */ - IgnoringThread = 'IGNORING_THREAD', + | 'IGNORING_THREAD' /** The User is not recieving notifications from this thread */ - None = 'NONE', + | 'NONE' /** The User is notified becuase they are watching the list */ - SubscribedToList = 'SUBSCRIBED_TO_LIST', + | 'SUBSCRIBED_TO_LIST' /** The User is notified because they are subscribed to the thread */ - SubscribedToThread = 'SUBSCRIBED_TO_THREAD', + | 'SUBSCRIBED_TO_THREAD' /** The User is notified because they chose custom settings for this thread. */ - SubscribedToThreadEvents = 'SUBSCRIBED_TO_THREAD_EVENTS', + | 'SUBSCRIBED_TO_THREAD_EVENTS' /** The User is notified because they chose custom settings for this thread. */ - SubscribedToThreadType = 'SUBSCRIBED_TO_THREAD_TYPE', + | 'SUBSCRIBED_TO_THREAD_TYPE' /** The subscription status is currently unavailable. */ - Unavailable = 'UNAVAILABLE' -} + | 'UNAVAILABLE'; /** A topic aggregates entities that are related to a subject. */ export type Topic = Node & Starrable & { @@ -32735,36 +32482,22 @@ export type TopicAuditEntryData = { }; /** Reason that the suggested topic is declined. */ -export enum TopicSuggestionDeclineReason { - /** - * The suggested topic is not relevant to the repository. - * @deprecated Suggested topics are no longer supported Removal on 2024-04-01 UTC. - */ - NotRelevant = 'NOT_RELEVANT', - /** - * The viewer does not like the suggested topic. - * @deprecated Suggested topics are no longer supported Removal on 2024-04-01 UTC. - */ - PersonalPreference = 'PERSONAL_PREFERENCE', - /** - * The suggested topic is too general for the repository. - * @deprecated Suggested topics are no longer supported Removal on 2024-04-01 UTC. - */ - TooGeneral = 'TOO_GENERAL', - /** - * The suggested topic is too specific for the repository (e.g. #ruby-on-rails-version-4-2-1). - * @deprecated Suggested topics are no longer supported Removal on 2024-04-01 UTC. - */ - TooSpecific = 'TOO_SPECIFIC' -} +export type TopicSuggestionDeclineReason = + /** The suggested topic is not relevant to the repository. */ + | 'NOT_RELEVANT' + /** The viewer does not like the suggested topic. */ + | 'PERSONAL_PREFERENCE' + /** The suggested topic is too general for the repository. */ + | 'TOO_GENERAL' + /** The suggested topic is too specific for the repository (e.g. #ruby-on-rails-version-4-2-1). */ + | 'TOO_SPECIFIC'; /** The possible states of a tracked issue. */ -export enum TrackedIssueStates { +export type TrackedIssueStates = /** The tracked issue is closed */ - Closed = 'CLOSED', + | 'CLOSED' /** The tracked issue is open */ - Open = 'OPEN' -} + | 'OPEN'; /** Autogenerated input type of TransferEnterpriseOrganization */ export type TransferEnterpriseOrganizationInput = { @@ -32876,14 +32609,13 @@ export type TreeEntry = { }; /** Filters by whether or not 2FA is enabled and if the method configured is considered secure or insecure. */ -export enum TwoFactorCredentialSecurityType { +export type TwoFactorCredentialSecurityType = /** No method of two-factor authentication. */ - Disabled = 'DISABLED', + | 'DISABLED' /** Has an insecure method of two-factor authentication. GitHub currently defines this as SMS two-factor authentication. */ - Insecure = 'INSECURE', + | 'INSECURE' /** Has only secure methods of two-factor authentication. */ - Secure = 'SECURE' -} + | 'SECURE'; /** Autogenerated input type of UnarchiveProjectV2Item */ export type UnarchiveProjectV2ItemInput = { @@ -35557,18 +35289,17 @@ export type UserWatchingArgs = { }; /** The possible durations that a user can be blocked for. */ -export enum UserBlockDuration { +export type UserBlockDuration = /** The user was blocked for 1 day */ - OneDay = 'ONE_DAY', + | 'ONE_DAY' /** The user was blocked for 30 days */ - OneMonth = 'ONE_MONTH', + | 'ONE_MONTH' /** The user was blocked for 7 days */ - OneWeek = 'ONE_WEEK', + | 'ONE_WEEK' /** The user was blocked permanently */ - Permanent = 'PERMANENT', + | 'PERMANENT' /** The user was blocked for 3 days */ - ThreeDays = 'THREE_DAYS' -} + | 'THREE_DAYS'; /** Represents a 'user_blocked' event on a given user. */ export type UserBlockedEvent = Node & { @@ -35842,18 +35573,16 @@ export type UserStatusOrder = { }; /** Properties by which user status connections can be ordered. */ -export enum UserStatusOrderField { +export type UserStatusOrderField = /** Order user statuses by when they were updated. */ - UpdatedAt = 'UPDATED_AT' -} + | 'UPDATED_AT'; /** Whether a user being viewed contains public or private information. */ -export enum UserViewType { +export type UserViewType = /** A user containing information only visible to the authenticated user. */ - Private = 'PRIVATE', + | 'PRIVATE' /** A user that is publicly visible. */ - Public = 'PUBLIC' -} + | 'PUBLIC'; /** A domain that can be verified or approved for an organization or an enterprise. */ export type VerifiableDomain = Node & { @@ -35921,12 +35650,11 @@ export type VerifiableDomainOrder = { }; /** Properties by which verifiable domain connections can be ordered. */ -export enum VerifiableDomainOrderField { +export type VerifiableDomainOrderField = /** Order verifiable domains by their creation date. */ - CreatedAt = 'CREATED_AT', + | 'CREATED_AT' /** Order verifiable domains by the domain name. */ - Domain = 'DOMAIN' -} + | 'DOMAIN'; /** Types that can own a verifiable domain. */ export type VerifiableDomainOwner = Enterprise | Organization; @@ -36130,24 +35858,22 @@ export type WorkflowRunOrder = { }; /** Properties by which workflow run connections can be ordered. */ -export enum WorkflowRunOrderField { +export type WorkflowRunOrderField = /** Order workflow runs by most recently created */ - CreatedAt = 'CREATED_AT' -} + | 'CREATED_AT'; /** The possible states for a workflow. */ -export enum WorkflowState { +export type WorkflowState = /** The workflow is active. */ - Active = 'ACTIVE', + | 'ACTIVE' /** The workflow was deleted from the git repository. */ - Deleted = 'DELETED', + | 'DELETED' /** The workflow was disabled by default on a fork. */ - DisabledFork = 'DISABLED_FORK', + | 'DISABLED_FORK' /** The workflow was disabled for inactivity in the repository. */ - DisabledInactivity = 'DISABLED_INACTIVITY', + | 'DISABLED_INACTIVITY' /** The workflow was disabled manually. */ - DisabledManually = 'DISABLED_MANUALLY' -} + | 'DISABLED_MANUALLY'; /** Require all changes made to a targeted branch to pass the specified workflows before they can be merged. */ export type WorkflowsParameters = { @@ -36168,6 +35894,24 @@ export type WorkflowsParametersInput = { export type _Entity = Issue; +type AuthorFields_Bot_Fragment = { __typename?: 'Bot', login: string, html_url: any, avatar_url: any, type: 'Bot' }; + +type AuthorFields_EnterpriseUserAccount_Fragment = { __typename?: 'EnterpriseUserAccount', login: string, html_url: any, avatar_url: any, type: 'EnterpriseUserAccount' }; + +type AuthorFields_Mannequin_Fragment = { __typename?: 'Mannequin', login: string, html_url: any, avatar_url: any, type: 'Mannequin' }; + +type AuthorFields_Organization_Fragment = { __typename?: 'Organization', login: string, html_url: any, avatar_url: any, type: 'Organization' }; + +type AuthorFields_User_Fragment = { __typename?: 'User', login: string, html_url: any, avatar_url: any, type: 'User' }; + +export type AuthorFieldsFragment = + | AuthorFields_Bot_Fragment + | AuthorFields_EnterpriseUserAccount_Fragment + | AuthorFields_Mannequin_Fragment + | AuthorFields_Organization_Fragment + | AuthorFields_User_Fragment +; + export type FetchDiscussionByNumberQueryVariables = Exact<{ owner: Scalars['String']['input']; name: Scalars['String']['input']; @@ -36180,50 +35924,88 @@ export type FetchDiscussionByNumberQueryVariables = Exact<{ export type FetchDiscussionByNumberQuery = { __typename?: 'Query', repository?: { __typename?: 'Repository', discussion?: { __typename: 'Discussion', number: number, title: string, stateReason?: DiscussionStateReason | null, isAnswered?: boolean | null, url: any, author?: - | { __typename?: 'Bot', login: string, url: any, avatar_url: any, type: 'Bot' } - | { __typename?: 'EnterpriseUserAccount', login: string, url: any, avatar_url: any, type: 'EnterpriseUserAccount' } - | { __typename?: 'Mannequin', login: string, url: any, avatar_url: any, type: 'Mannequin' } - | { __typename?: 'Organization', login: string, url: any, avatar_url: any, type: 'Organization' } - | { __typename?: 'User', login: string, url: any, avatar_url: any, type: 'User' } - | null, comments: { __typename?: 'DiscussionCommentConnection', totalCount: number, nodes?: Array<{ __typename?: 'DiscussionComment', databaseId?: number | null, createdAt: any, replies: { __typename?: 'DiscussionCommentConnection', nodes?: Array<{ __typename?: 'DiscussionComment', databaseId?: number | null, createdAt: any, author?: - | { __typename?: 'Bot', login: string, url: any, avatar_url: any, type: 'Bot' } - | { __typename?: 'EnterpriseUserAccount', login: string, url: any, avatar_url: any, type: 'EnterpriseUserAccount' } - | { __typename?: 'Mannequin', login: string, url: any, avatar_url: any, type: 'Mannequin' } - | { __typename?: 'Organization', login: string, url: any, avatar_url: any, type: 'Organization' } - | { __typename?: 'User', login: string, url: any, avatar_url: any, type: 'User' } + | { __typename?: 'Bot', login: string, html_url: any, avatar_url: any, type: 'Bot' } + | { __typename?: 'EnterpriseUserAccount', login: string, html_url: any, avatar_url: any, type: 'EnterpriseUserAccount' } + | { __typename?: 'Mannequin', login: string, html_url: any, avatar_url: any, type: 'Mannequin' } + | { __typename?: 'Organization', login: string, html_url: any, avatar_url: any, type: 'Organization' } + | { __typename?: 'User', login: string, html_url: any, avatar_url: any, type: 'User' } + | null, comments: { __typename?: 'DiscussionCommentConnection', totalCount: number, nodes?: Array<{ __typename?: 'DiscussionComment', databaseId?: number | null, createdAt: any, url: any, replies: { __typename?: 'DiscussionCommentConnection', totalCount: number, nodes?: Array<{ __typename?: 'DiscussionComment', databaseId?: number | null, createdAt: any, url: any, author?: + | { __typename?: 'Bot', login: string, html_url: any, avatar_url: any, type: 'Bot' } + | { __typename?: 'EnterpriseUserAccount', login: string, html_url: any, avatar_url: any, type: 'EnterpriseUserAccount' } + | { __typename?: 'Mannequin', login: string, html_url: any, avatar_url: any, type: 'Mannequin' } + | { __typename?: 'Organization', login: string, html_url: any, avatar_url: any, type: 'Organization' } + | { __typename?: 'User', login: string, html_url: any, avatar_url: any, type: 'User' } | null } | null> | null }, author?: - | { __typename?: 'Bot', login: string, url: any, avatar_url: any, type: 'Bot' } - | { __typename?: 'EnterpriseUserAccount', login: string, url: any, avatar_url: any, type: 'EnterpriseUserAccount' } - | { __typename?: 'Mannequin', login: string, url: any, avatar_url: any, type: 'Mannequin' } - | { __typename?: 'Organization', login: string, url: any, avatar_url: any, type: 'Organization' } - | { __typename?: 'User', login: string, url: any, avatar_url: any, type: 'User' } + | { __typename?: 'Bot', login: string, html_url: any, avatar_url: any, type: 'Bot' } + | { __typename?: 'EnterpriseUserAccount', login: string, html_url: any, avatar_url: any, type: 'EnterpriseUserAccount' } + | { __typename?: 'Mannequin', login: string, html_url: any, avatar_url: any, type: 'Mannequin' } + | { __typename?: 'Organization', login: string, html_url: any, avatar_url: any, type: 'Organization' } + | { __typename?: 'User', login: string, html_url: any, avatar_url: any, type: 'User' } | null } | null> | null }, labels?: { __typename?: 'LabelConnection', nodes?: Array<{ __typename?: 'Label', name: string } | null> | null } | null } | null } | null }; -type AuthorFields_Bot_Fragment = { __typename?: 'Bot', login: string, url: any, avatar_url: any, type: 'Bot' }; +export type CommentFieldsFragment = { __typename?: 'DiscussionComment', databaseId?: number | null, createdAt: any, url: any, author?: + | { __typename?: 'Bot', login: string, html_url: any, avatar_url: any, type: 'Bot' } + | { __typename?: 'EnterpriseUserAccount', login: string, html_url: any, avatar_url: any, type: 'EnterpriseUserAccount' } + | { __typename?: 'Mannequin', login: string, html_url: any, avatar_url: any, type: 'Mannequin' } + | { __typename?: 'Organization', login: string, html_url: any, avatar_url: any, type: 'Organization' } + | { __typename?: 'User', login: string, html_url: any, avatar_url: any, type: 'User' } + | null }; -type AuthorFields_EnterpriseUserAccount_Fragment = { __typename?: 'EnterpriseUserAccount', login: string, url: any, avatar_url: any, type: 'EnterpriseUserAccount' }; +export type FetchIssueByNumberQueryVariables = Exact<{ + owner: Scalars['String']['input']; + name: Scalars['String']['input']; + number: Scalars['Int']['input']; + lastComments?: InputMaybe; + firstLabels?: InputMaybe; +}>; -type AuthorFields_Mannequin_Fragment = { __typename?: 'Mannequin', login: string, url: any, avatar_url: any, type: 'Mannequin' }; -type AuthorFields_Organization_Fragment = { __typename?: 'Organization', login: string, url: any, avatar_url: any, type: 'Organization' }; +export type FetchIssueByNumberQuery = { __typename?: 'Query', repository?: { __typename?: 'Repository', issue?: { __typename: 'Issue', number: number, title: string, url: any, state: IssueState, stateReason?: IssueStateReason | null, milestone?: { __typename?: 'Milestone', state: MilestoneState, title: string } | null, author?: + | { __typename?: 'Bot', login: string, html_url: any, avatar_url: any, type: 'Bot' } + | { __typename?: 'EnterpriseUserAccount', login: string, html_url: any, avatar_url: any, type: 'EnterpriseUserAccount' } + | { __typename?: 'Mannequin', login: string, html_url: any, avatar_url: any, type: 'Mannequin' } + | { __typename?: 'Organization', login: string, html_url: any, avatar_url: any, type: 'Organization' } + | { __typename?: 'User', login: string, html_url: any, avatar_url: any, type: 'User' } + | null, comments: { __typename?: 'IssueCommentConnection', totalCount: number, nodes?: Array<{ __typename?: 'IssueComment', url: any, author?: + | { __typename?: 'Bot', login: string, html_url: any, avatar_url: any, type: 'Bot' } + | { __typename?: 'EnterpriseUserAccount', login: string, html_url: any, avatar_url: any, type: 'EnterpriseUserAccount' } + | { __typename?: 'Mannequin', login: string, html_url: any, avatar_url: any, type: 'Mannequin' } + | { __typename?: 'Organization', login: string, html_url: any, avatar_url: any, type: 'Organization' } + | { __typename?: 'User', login: string, html_url: any, avatar_url: any, type: 'User' } + | null } | null> | null }, labels?: { __typename?: 'LabelConnection', nodes?: Array<{ __typename?: 'Label', name: string } | null> | null } | null } | null } | null }; -type AuthorFields_User_Fragment = { __typename?: 'User', login: string, url: any, avatar_url: any, type: 'User' }; +export type MilestoneFieldsFragment = { __typename?: 'Milestone', state: MilestoneState, title: string }; -export type AuthorFieldsFragment = - | AuthorFields_Bot_Fragment - | AuthorFields_EnterpriseUserAccount_Fragment - | AuthorFields_Mannequin_Fragment - | AuthorFields_Organization_Fragment - | AuthorFields_User_Fragment -; +export type FetchPullRequestByNumberQueryVariables = Exact<{ + owner: Scalars['String']['input']; + name: Scalars['String']['input']; + number: Scalars['Int']['input']; + firstLabels?: InputMaybe; + lastComments?: InputMaybe; + lastReviews?: InputMaybe; + firstClosingIssues?: InputMaybe; +}>; -export type CommentFieldsFragment = { __typename?: 'DiscussionComment', databaseId?: number | null, createdAt: any, author?: - | { __typename?: 'Bot', login: string, url: any, avatar_url: any, type: 'Bot' } - | { __typename?: 'EnterpriseUserAccount', login: string, url: any, avatar_url: any, type: 'EnterpriseUserAccount' } - | { __typename?: 'Mannequin', login: string, url: any, avatar_url: any, type: 'Mannequin' } - | { __typename?: 'Organization', login: string, url: any, avatar_url: any, type: 'Organization' } - | { __typename?: 'User', login: string, url: any, avatar_url: any, type: 'User' } - | null }; + +export type FetchPullRequestByNumberQuery = { __typename?: 'Query', repository?: { __typename?: 'Repository', pullRequest?: { __typename: 'PullRequest', number: number, title: string, url: any, state: PullRequestState, merged: boolean, isDraft: boolean, isInMergeQueue: boolean, milestone?: { __typename?: 'Milestone', state: MilestoneState, title: string } | null, author?: + | { __typename?: 'Bot', login: string, html_url: any, avatar_url: any, type: 'Bot' } + | { __typename?: 'EnterpriseUserAccount', login: string, html_url: any, avatar_url: any, type: 'EnterpriseUserAccount' } + | { __typename?: 'Mannequin', login: string, html_url: any, avatar_url: any, type: 'Mannequin' } + | { __typename?: 'Organization', login: string, html_url: any, avatar_url: any, type: 'Organization' } + | { __typename?: 'User', login: string, html_url: any, avatar_url: any, type: 'User' } + | null, comments: { __typename?: 'IssueCommentConnection', totalCount: number, nodes?: Array<{ __typename?: 'IssueComment', url: any, author?: + | { __typename?: 'Bot', login: string, html_url: any, avatar_url: any, type: 'Bot' } + | { __typename?: 'EnterpriseUserAccount', login: string, html_url: any, avatar_url: any, type: 'EnterpriseUserAccount' } + | { __typename?: 'Mannequin', login: string, html_url: any, avatar_url: any, type: 'Mannequin' } + | { __typename?: 'Organization', login: string, html_url: any, avatar_url: any, type: 'Organization' } + | { __typename?: 'User', login: string, html_url: any, avatar_url: any, type: 'User' } + | null } | null> | null }, reviews?: { __typename?: 'PullRequestReviewConnection', totalCount: number, nodes?: Array<{ __typename?: 'PullRequestReview', state: PullRequestReviewState, author?: + | { __typename?: 'Bot', login: string } + | { __typename?: 'EnterpriseUserAccount', login: string } + | { __typename?: 'Mannequin', login: string } + | { __typename?: 'Organization', login: string } + | { __typename?: 'User', login: string } + | null } | null> | null } | null, labels?: { __typename?: 'LabelConnection', nodes?: Array<{ __typename?: 'Label', name: string } | null> | null } | null, closingIssuesReferences?: { __typename?: 'IssueConnection', nodes?: Array<{ __typename?: 'Issue', number: number } | null> | null } | null } | null } | null }; export class TypedDocumentString extends String @@ -36246,7 +36028,7 @@ export class TypedDocumentString export const AuthorFieldsFragmentDoc = new TypedDocumentString(` fragment AuthorFields on Actor { login - url + html_url: url avatar_url: avatarUrl type: __typename } @@ -36258,13 +36040,20 @@ export const CommentFieldsFragmentDoc = new TypedDocumentString(` author { ...AuthorFields } + url } fragment AuthorFields on Actor { login - url + html_url: url avatar_url: avatarUrl type: __typename }`, {"fragmentName":"CommentFields"}) as unknown as TypedDocumentString; +export const MilestoneFieldsFragmentDoc = new TypedDocumentString(` + fragment MilestoneFields on Milestone { + state + title +} + `, {"fragmentName":"MilestoneFields"}) as unknown as TypedDocumentString; export const FetchDiscussionByNumberDocument = new TypedDocumentString(` query FetchDiscussionByNumber($owner: String!, $name: String!, $number: Int!, $lastComments: Int, $lastReplies: Int, $firstLabels: Int, $includeIsAnswered: Boolean!) { repository(owner: $owner, name: $name) { @@ -36283,6 +36072,7 @@ export const FetchDiscussionByNumberDocument = new TypedDocumentString(` nodes { ...CommentFields replies(last: $lastReplies) { + totalCount nodes { ...CommentFields } @@ -36299,7 +36089,7 @@ export const FetchDiscussionByNumberDocument = new TypedDocumentString(` } fragment AuthorFields on Actor { login - url + html_url: url avatar_url: avatarUrl type: __typename } @@ -36309,4 +36099,107 @@ fragment CommentFields on DiscussionComment { author { ...AuthorFields } -}`) as unknown as TypedDocumentString; \ No newline at end of file + url +}`) as unknown as TypedDocumentString; +export const FetchIssueByNumberDocument = new TypedDocumentString(` + query FetchIssueByNumber($owner: String!, $name: String!, $number: Int!, $lastComments: Int, $firstLabels: Int) { + repository(owner: $owner, name: $name) { + issue(number: $number) { + __typename + number + title + url + state + stateReason + milestone { + ...MilestoneFields + } + author { + ...AuthorFields + } + comments(last: $lastComments) { + totalCount + nodes { + url + author { + ...AuthorFields + } + } + } + labels(first: $firstLabels) { + nodes { + name + } + } + } + } +} + fragment AuthorFields on Actor { + login + html_url: url + avatar_url: avatarUrl + type: __typename +} +fragment MilestoneFields on Milestone { + state + title +}`) as unknown as TypedDocumentString; +export const FetchPullRequestByNumberDocument = new TypedDocumentString(` + query FetchPullRequestByNumber($owner: String!, $name: String!, $number: Int!, $firstLabels: Int, $lastComments: Int, $lastReviews: Int, $firstClosingIssues: Int) { + repository(owner: $owner, name: $name) { + pullRequest(number: $number) { + __typename + number + title + url + state + merged + isDraft + isInMergeQueue + milestone { + ...MilestoneFields + } + author { + ...AuthorFields + } + comments(last: $lastComments) { + totalCount + nodes { + url + author { + ...AuthorFields + } + } + } + reviews(last: $lastReviews) { + totalCount + nodes { + state + author { + login + } + } + } + labels(first: $firstLabels) { + nodes { + name + } + } + closingIssuesReferences(first: $firstClosingIssues) { + nodes { + number + } + } + } + } +} + fragment AuthorFields on Actor { + login + html_url: url + avatar_url: avatarUrl + type: __typename +} +fragment MilestoneFields on Milestone { + state + title +}`) as unknown as TypedDocumentString; \ No newline at end of file diff --git a/src/renderer/utils/api/graphql/issue.graphql b/src/renderer/utils/api/graphql/issue.graphql new file mode 100644 index 000000000..63af6ec18 --- /dev/null +++ b/src/renderer/utils/api/graphql/issue.graphql @@ -0,0 +1,43 @@ +query FetchIssueByNumber( + $owner: String! + $name: String! + $number: Int! + $lastComments: Int + $firstLabels: Int +) { + repository(owner: $owner, name: $name) { + issue(number: $number) { + __typename + number + title + url + state + stateReason + milestone { + ...MilestoneFields + } + author { + ...AuthorFields + } + comments(last: $lastComments) { + totalCount + nodes { + url + author { + ...AuthorFields + } + } + } + labels(first: $firstLabels) { + nodes { + name + } + } + } + } +} + +fragment MilestoneFields on Milestone { + state + title +} diff --git a/src/renderer/utils/api/graphql/pull.graphql b/src/renderer/utils/api/graphql/pull.graphql new file mode 100644 index 000000000..fa33c9f0c --- /dev/null +++ b/src/renderer/utils/api/graphql/pull.graphql @@ -0,0 +1,61 @@ +query FetchPullRequestByNumber( + $owner: String! + $name: String! + $number: Int! + $firstLabels: Int + $lastComments: Int + $lastReviews: Int + $firstClosingIssues: Int +) { + repository(owner: $owner, name: $name) { + pullRequest(number: $number) { + __typename + number + title + url + state + merged + isDraft + isInMergeQueue + milestone { + ...MilestoneFields + } + author { + ...AuthorFields + } + comments(last: $lastComments) { + totalCount + nodes { + url + author { + ...AuthorFields + } + } + } + reviews(last: $lastReviews) { + totalCount + nodes { + state + author { + login + } + } + } + labels(first: $firstLabels) { + nodes { + name + } + } + closingIssuesReferences(first: $firstClosingIssues) { + nodes { + number + } + } + } + } +} + +fragment MilestoneFields on Milestone { + state + title +} diff --git a/src/renderer/utils/helpers.test.ts b/src/renderer/utils/helpers.test.ts index d29937918..0bd6c7f3f 100644 --- a/src/renderer/utils/helpers.test.ts +++ b/src/renderer/utils/helpers.test.ts @@ -4,23 +4,15 @@ import { ChevronRightIcon, } from '@primer/octicons-react'; -import type { ExecutionResult } from 'graphql'; - -import { createPartialMockNotification } from '../__mocks__/notifications-mocks'; +import { mockToken } from '../__mocks__/state-mocks'; import type { Hostname, Link } from '../types'; -import type { SubjectType } from '../typesGitHub'; -import * as logger from '../utils/logger'; -import { - mockDiscussionByNumberGraphQLResponse, - mockSingleNotification, -} from './api/__mocks__/response-mocks'; +import type { Subject, SubjectType } from '../typesGitHub'; +import { mockSingleNotification } from './api/__mocks__/response-mocks'; import * as apiClient from './api/client'; -import type { FetchDiscussionByNumberQuery } from './api/graphql/generated/graphql'; import { generateGitHubWebUrl, generateNotificationReferrerId, getChevronDetails, - getDefaultURLForType, getPlatformFromHostname, isEnterpriseServerHost, } from './helpers'; @@ -83,33 +75,44 @@ describe('renderer/utils/helpers.ts', () => { jest.clearAllMocks(); }); - it('Subject Latest Comment Url: when not null, fetch latest comment html url', async () => { + it('Subject HTML URL: prefer if available from enrichment stage', async () => { + const mockSubjectHtmlUrl = 'https://gitify.io/' as Link; + const mockSubjectUrl = + 'https://api.github.com/repos/gitify-app/notifications-test/issues/1' as Link; + const mockLatestCommentUrl = + 'https://api.github.com/repos/gitify-app/notifications-test/issues/comments/302888448' as Link; + const subject = { title: 'generate github web url unit tests', - url: 'https://api.github.com/repos/gitify-app/notifications-test/issues/1' as Link, - latest_comment_url: - 'https://api.github.com/repos/gitify-app/notifications-test/issues/comments/302888448' as Link, + url: mockSubjectUrl, + latest_comment_url: mockLatestCommentUrl, type: 'Issue' as SubjectType, - }; - - getHtmlUrlSpy.mockResolvedValue(mockHtmlUrl); + htmlUrl: mockSubjectHtmlUrl, + } as Subject; const result = await generateGitHubWebUrl({ ...mockSingleNotification, subject: subject, }); - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(1); - expect(result).toBe(`${mockHtmlUrl}?${mockNotificationReferrer}`); + expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); + expect(result).toBe(`${mockSubjectHtmlUrl}?${mockNotificationReferrer}`); }); - it('Subject Url: when no latest comment url available, fetch subject html url', async () => { + it('Subject Latest Comment Url: when not null, fetch latest comment html url', async () => { + const mockSubjectHtmlUrl = null; + const mockSubjectUrl = + 'https://api.github.com/repos/gitify-app/notifications-test/issues/1' as Link; + const mockLatestCommentUrl = + 'https://api.github.com/repos/gitify-app/notifications-test/issues/comments/302888448' as Link; + const subject = { title: 'generate github web url unit tests', - url: 'https://api.github.com/repos/gitify-app/notifications-test/issues/1' as Link, - latest_comment_url: null, + url: mockSubjectUrl, + latest_comment_url: mockLatestCommentUrl, type: 'Issue' as SubjectType, - }; + htmlUrl: mockSubjectHtmlUrl, + } as Subject; getHtmlUrlSpy.mockResolvedValue(mockHtmlUrl); @@ -119,510 +122,37 @@ describe('renderer/utils/helpers.ts', () => { }); expect(getHtmlUrlSpy).toHaveBeenCalledTimes(1); + expect(getHtmlUrlSpy).toHaveBeenCalledWith( + mockLatestCommentUrl, + mockToken, + ); expect(result).toBe(`${mockHtmlUrl}?${mockNotificationReferrer}`); }); - describe('Check Suite URLs', () => { - it('successful workflow', async () => { - const subject = { - title: 'Demo workflow run succeeded for main branch', - url: null, - latest_comment_url: null, - type: 'CheckSuite' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/actions?query=workflow%3A%22Demo%22+is%3Asuccess+branch%3Amain&${mockNotificationReferrer}`, - ); - }); - - it('failed workflow', async () => { - const subject = { - title: 'Demo workflow run failed for main branch', - url: null, - latest_comment_url: null, - type: 'CheckSuite' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/actions?query=workflow%3A%22Demo%22+is%3Afailure+branch%3Amain&${mockNotificationReferrer}`, - ); - }); - - it('failed workflow multiple attempts', async () => { - const subject = { - title: 'Demo workflow run, Attempt #3 failed for main branch', - url: null, - latest_comment_url: null, - type: 'CheckSuite' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/actions?query=workflow%3A%22Demo%22+is%3Afailure+branch%3Amain&${mockNotificationReferrer}`, - ); - }); - - it('skipped workflow', async () => { - const subject = { - title: 'Demo workflow run skipped for main branch', - url: null, - latest_comment_url: null, - type: 'CheckSuite' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/actions?query=workflow%3A%22Demo%22+is%3Askipped+branch%3Amain&${mockNotificationReferrer}`, - ); - }); - - it('unhandled workflow scenario', async () => { - const subject = { - title: 'unhandled workflow scenario', - url: null, - latest_comment_url: null, - type: 'CheckSuite' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/actions?${mockNotificationReferrer}`, - ); - }); - - it('unhandled status scenario', async () => { - const subject = { - title: 'Demo workflow run unhandled-status for main branch', - url: null, - latest_comment_url: null, - type: 'CheckSuite' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/actions?query=workflow%3A%22Demo%22+branch%3Amain&${mockNotificationReferrer}`, - ); - }); - - it('unhandled check suite scenario', async () => { - const subject = { - title: 'Unhandled scenario', - url: null, - latest_comment_url: null, - type: 'CheckSuite' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/actions?${mockNotificationReferrer}`, - ); - }); - }); - - describe('Discussions URLs', () => { - const fetchDiscussionByNumberSpy = jest.spyOn( - apiClient, - 'fetchDiscussionByNumber', - ); - - it('when no discussion found via graphql api, default to base repository discussion url', async () => { - const subject = { - title: 'generate github web url unit tests', - url: null, - latest_comment_url: null, - type: 'Discussion' as SubjectType, - }; - - fetchDiscussionByNumberSpy.mockResolvedValue({ - data: { repository: { discussion: null } }, - } as any); - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(fetchDiscussionByNumberSpy).toHaveBeenCalledTimes(1); - expect(result).toBe( - `${mockSingleNotification.repository.html_url}/discussions?${mockNotificationReferrer}`, - ); - }); - - it('when error fetching discussion via graphql api, default to base repository discussion url', async () => { - const rendererLogErrorSpy = jest - .spyOn(logger, 'rendererLogError') - .mockImplementation(); - - const subject = { - title: '1.16.0', - url: null, - latest_comment_url: null, - type: 'Discussion' as SubjectType, - }; - - fetchDiscussionByNumberSpy.mockResolvedValue({ - data: null, - errors: [ - { - message: 'Something failed', - }, - ], - } as unknown as ExecutionResult); - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(fetchDiscussionByNumberSpy).toHaveBeenCalledTimes(1); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/discussions?${mockNotificationReferrer}`, - ); - expect(rendererLogErrorSpy).toHaveBeenCalledTimes(1); - }); - - it('when discussion found via graphql api, link to matching discussion and comment hash', async () => { - const subject = { - title: '1.16.0', - url: null, - latest_comment_url: null, - type: 'Discussion' as SubjectType, - }; - - fetchDiscussionByNumberSpy.mockResolvedValue({ - data: mockDiscussionByNumberGraphQLResponse, - } as ExecutionResult); - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(fetchDiscussionByNumberSpy).toHaveBeenCalledTimes(1); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/discussions/612?${mockNotificationReferrer}#discussioncomment-2300902`, - ); - }); - - it('when api throws error, default to base repository discussion url', async () => { - const rendererLogErrorSpy = jest - .spyOn(logger, 'rendererLogError') - .mockImplementation(); - - const subject = { - title: '1.16.0', - url: null, - latest_comment_url: null, - type: 'Discussion' as SubjectType, - }; - - fetchDiscussionByNumberSpy.mockRejectedValue( - new Error('Something failed'), - ); - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(fetchDiscussionByNumberSpy).toHaveBeenCalledTimes(1); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/discussions?${mockNotificationReferrer}`, - ); - expect(rendererLogErrorSpy).toHaveBeenCalledTimes(1); - }); - }); + it('Subject Url: when no latest comment url available, fetch subject html url', async () => { + const mockSubjectHtmlUrl = null; + const mockSubjectUrl = + 'https://api.github.com/repos/gitify-app/notifications-test/issues/1' as Link; + const mockLatestCommentUrl = null; - it('Repository Invitation url', async () => { const subject = { - title: - 'Invitation to join gitify-app/notifications-test from unit-tests', - url: null, - latest_comment_url: null, - type: 'RepositoryInvitation' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/invitations?${mockNotificationReferrer}`, - ); - }); + title: 'generate github web url unit tests', + url: mockSubjectUrl, + latest_comment_url: mockLatestCommentUrl, + type: 'Issue' as SubjectType, + htmlUrl: mockSubjectHtmlUrl, + } as Subject; - it('Repository Dependabot Alerts Thread url', async () => { - const subject = { - title: 'Your repository has dependencies with security vulnerabilities', - url: null, - latest_comment_url: null, - type: 'RepositoryDependabotAlertsThread' as SubjectType, - }; + getHtmlUrlSpy.mockResolvedValue(mockHtmlUrl); const result = await generateGitHubWebUrl({ ...mockSingleNotification, subject: subject, }); - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/security/dependabot?${mockNotificationReferrer}`, - ); - }); - - describe('Workflow Run URLs', () => { - it('approval requested', async () => { - const subject = { - title: 'some-user requested your review to deploy to an environment', - url: null, - latest_comment_url: null, - type: 'WorkflowRun' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/actions?query=is%3Awaiting&${mockNotificationReferrer}`, - ); - }); - - it('unhandled status/action scenario', async () => { - const subject = { - title: - 'some-user requested your unhandled-action to deploy to an environment', - url: null, - latest_comment_url: null, - type: 'WorkflowRun' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/actions?${mockNotificationReferrer}`, - ); - }); - - it('unhandled workflow scenario', async () => { - const subject = { - title: 'some unhandled scenario', - url: null, - latest_comment_url: null, - type: 'WorkflowRun' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/actions?${mockNotificationReferrer}`, - ); - }); - }); - - describe('defaults web urls', () => { - it('issues - defaults when no urls present in notification', async () => { - const subject = { - title: 'generate github web url unit tests', - url: null, - latest_comment_url: null, - type: 'Issue' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/issues?${mockNotificationReferrer}`, - ); - }); - - it('pull requests - defaults when no urls present in notification', async () => { - const subject = { - title: 'generate github web url unit tests', - url: null, - latest_comment_url: null, - type: 'PullRequest' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/pulls?${mockNotificationReferrer}`, - ); - }); - - it('other - defaults when no urls present in notification', async () => { - const subject = { - title: 'generate github web url unit tests', - url: null, - latest_comment_url: null, - type: 'Commit' as SubjectType, - }; - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(0); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test?${mockNotificationReferrer}`, - ); - }); - - it('defaults when exception handled during specialized html enrichment process', async () => { - const rendererLogErrorSpy = jest - .spyOn(logger, 'rendererLogError') - .mockImplementation(); - - const subject = { - title: 'generate github web url unit tests', - url: 'https://api.github.com/repos/gitify-app/notifications-test/issues/1' as Link, - latest_comment_url: null as Link, - type: 'Issue' as SubjectType, - }; - - getHtmlUrlSpy.mockRejectedValue(new Error('Test error')); - - const result = await generateGitHubWebUrl({ - ...mockSingleNotification, - subject: subject, - }); - - expect(getHtmlUrlSpy).toHaveBeenCalledTimes(1); - expect(result).toBe( - `https://github.com/gitify-app/notifications-test/issues?${mockNotificationReferrer}`, - ); - expect(rendererLogErrorSpy).toHaveBeenCalledTimes(1); - }); - }); - }); - - describe('getDefaultURLForType', () => { - const mockUrl = 'https://github.com/gitify-app/notifications-test' as Link; - - it('discussions', () => { - const mockNotification = createPartialMockNotification( - { type: 'Discussion' }, - { html_url: mockUrl }, - ); - - expect(getDefaultURLForType(mockNotification)).toEqual( - 'https://github.com/gitify-app/notifications-test/discussions', - ); - }); - - it('issues', () => { - const mockNotification = createPartialMockNotification( - { type: 'Issue' }, - { html_url: mockUrl }, - ); - - expect(getDefaultURLForType(mockNotification)).toEqual( - 'https://github.com/gitify-app/notifications-test/issues', - ); - }); - - it('pull requests', () => { - const mockNotification = createPartialMockNotification( - { type: 'PullRequest' }, - { html_url: mockUrl }, - ); - - expect(getDefaultURLForType(mockNotification)).toEqual( - 'https://github.com/gitify-app/notifications-test/pulls', - ); - }); - - it('repository invitation', () => { - const mockNotification = createPartialMockNotification( - { type: 'RepositoryInvitation' }, - { html_url: mockUrl }, - ); - - expect(getDefaultURLForType(mockNotification)).toEqual( - 'https://github.com/gitify-app/notifications-test/invitations', - ); - }); - - it('repository dependabot alert thread', () => { - const mockNotification = createPartialMockNotification( - { type: 'RepositoryDependabotAlertsThread' }, - { html_url: mockUrl }, - ); - - expect(getDefaultURLForType(mockNotification)).toEqual( - 'https://github.com/gitify-app/notifications-test/security/dependabot', - ); - }); - - it('default web urls', () => { - const mockNotification = createPartialMockNotification( - { type: 'Commit' }, - { html_url: mockUrl }, - ); - - expect(getDefaultURLForType(mockNotification)).toEqual( - 'https://github.com/gitify-app/notifications-test', - ); + expect(getHtmlUrlSpy).toHaveBeenCalledTimes(1); + expect(getHtmlUrlSpy).toHaveBeenCalledWith(mockSubjectUrl, mockToken); + expect(result).toBe(`${mockHtmlUrl}?${mockNotificationReferrer}`); }); }); diff --git a/src/renderer/utils/helpers.ts b/src/renderer/utils/helpers.ts index 5a8bf5803..9580fc116 100644 --- a/src/renderer/utils/helpers.ts +++ b/src/renderer/utils/helpers.ts @@ -7,12 +7,10 @@ import { import { Constants } from '../constants'; import type { Chevron, Hostname, Link } from '../types'; import type { Notification } from '../typesGitHub'; -import { fetchDiscussionByNumber, getHtmlUrl } from './api/client'; +import { getHtmlUrl } from './api/client'; import type { PlatformType } from './auth/types'; import { rendererLogError } from './logger'; -import { getCheckSuiteAttributes } from './notifications/handlers/checkSuite'; -import { getClosestDiscussionCommentOrReply } from './notifications/handlers/discussion'; -import { getWorkflowRunAttributes } from './notifications/handlers/workflowRun'; +import { createNotificationHandler } from './notifications/handlers'; export function getPlatformFromHostname(hostname: string): PlatformType { return hostname.endsWith(Constants.DEFAULT_AUTH_OPTIONS.hostname) @@ -31,40 +29,6 @@ export function generateNotificationReferrerId( return btoa(raw); } -export function getCheckSuiteUrl(notification: Notification): Link { - const filters = []; - - const checkSuiteAttributes = getCheckSuiteAttributes(notification); - - if (checkSuiteAttributes?.workflowName) { - filters.push( - `workflow:"${checkSuiteAttributes.workflowName.replaceAll(' ', '+')}"`, - ); - } - - if (checkSuiteAttributes?.status) { - filters.push(`is:${checkSuiteAttributes.status}`); - } - - if (checkSuiteAttributes?.branchName) { - filters.push(`branch:${checkSuiteAttributes.branchName}`); - } - - return actionsURL(notification.repository.html_url, filters); -} - -export function getWorkflowRunUrl(notification: Notification): Link { - const filters = []; - - const workflowRunAttributes = getWorkflowRunAttributes(notification); - - if (workflowRunAttributes?.status) { - filters.push(`is:${workflowRunAttributes.status}`); - } - - return actionsURL(notification.repository.html_url, filters); -} - /** * Construct a GitHub Actions URL for a repository with optional filters. */ @@ -80,64 +44,35 @@ export function actionsURL(repositoryURL: string, filters: string[]): Link { return url.toString().replaceAll('%2B', '+') as Link; } -async function getDiscussionUrl(notification: Notification): Promise { - const url = new URL(notification.repository.html_url); - url.pathname += '/discussions'; - - const response = await fetchDiscussionByNumber(notification); - const discussion = response.data.repository.discussion; - - if (discussion) { - url.href = discussion.url; - - const closestComment = getClosestDiscussionCommentOrReply( - notification, - discussion.comments.nodes, - ); - if (closestComment) { - url.hash = `#discussioncomment-${closestComment.databaseId}`; - } - } - - return url.toString() as Link; -} - export async function generateGitHubWebUrl( notification: Notification, ): Promise { - const url = new URL(getDefaultURLForType(notification)); - - try { - switch (notification.subject.type) { - case 'CheckSuite': - url.href = getCheckSuiteUrl(notification); - break; - case 'Discussion': - url.href = await getDiscussionUrl(notification); - break; - case 'WorkflowRun': - url.href = getWorkflowRunUrl(notification); - break; - default: - if (notification.subject.latest_comment_url) { - url.href = await getHtmlUrl( - notification.subject.latest_comment_url, - notification.account.token, - ); - } else if (notification.subject.url) { - url.href = await getHtmlUrl( - notification.subject.url, - notification.account.token, - ); - } + const handler = createNotificationHandler(notification); + const url = new URL(handler.defaultUrl(notification)); + + if (notification.subject.htmlUrl) { + url.href = notification.subject.htmlUrl; + } else { + try { + if (notification.subject.latest_comment_url) { + url.href = await getHtmlUrl( + notification.subject.latest_comment_url, + notification.account.token, + ); + } else if (notification.subject.url) { + url.href = await getHtmlUrl( + notification.subject.url, + notification.account.token, + ); + } + } catch (err) { + rendererLogError( + 'generateGitHubWebUrl', + 'Failed to resolve specific notification html url for', + err, + notification, + ); } - } catch (err) { - rendererLogError( - 'generateGitHubWebUrl', - 'Failed to resolve specific notification html url for', - err, - notification, - ); } url.searchParams.set( @@ -148,32 +83,6 @@ export async function generateGitHubWebUrl( return url.toString() as Link; } -export function getDefaultURLForType(notification: Notification) { - const url = new URL(notification.repository.html_url); - - switch (notification.subject.type) { - case 'Discussion': - url.pathname += '/discussions'; - break; - case 'Issue': - url.pathname += '/issues'; - break; - case 'PullRequest': - url.pathname += '/pulls'; - break; - case 'RepositoryInvitation': - url.pathname += '/invitations'; - break; - case 'RepositoryDependabotAlertsThread': - url.pathname += '/security/dependabot'; - break; - default: - break; - } - - return url.href; -} - export function getChevronDetails( hasNotifications: boolean, isVisible: boolean, diff --git a/src/renderer/utils/icons.test.ts b/src/renderer/utils/icons.test.ts index 1ca83f48e..eae477ea1 100644 --- a/src/renderer/utils/icons.test.ts +++ b/src/renderer/utils/icons.test.ts @@ -7,8 +7,7 @@ import { OrganizationIcon, } from '@primer/octicons-react'; -import { IconColor } from '../types'; -import type { GitifyPullRequestReview } from '../typesGitHub'; +import { type GitifyPullRequestReview, IconColor } from '../types'; import { getAuthMethodIcon, getDefaultUserIcon, diff --git a/src/renderer/utils/icons.ts b/src/renderer/utils/icons.ts index 1f472ff5d..c682e8b72 100644 --- a/src/renderer/utils/icons.ts +++ b/src/renderer/utils/icons.ts @@ -14,8 +14,12 @@ import { ServerIcon, } from '@primer/octicons-react'; -import { IconColor, type PullRequestApprovalIcon } from '../types'; -import type { GitifyPullRequestReview, UserType } from '../typesGitHub'; +import { + type GitifyPullRequestReview, + IconColor, + type PullRequestApprovalIcon, +} from '../types'; +import type { UserType } from '../typesGitHub'; import type { AuthMethod, PlatformType } from './auth/types'; export function getPullRequestReviewIcon( diff --git a/src/renderer/utils/links.ts b/src/renderer/utils/links.ts index 88ce44109..4b0beb4f5 100644 --- a/src/renderer/utils/links.ts +++ b/src/renderer/utils/links.ts @@ -1,8 +1,8 @@ import { APPLICATION } from '../../shared/constants'; import { Constants } from '../constants'; -import type { Account, Hostname, Link } from '../types'; -import type { Notification, Repository, SubjectUser } from '../typesGitHub'; +import type { Account, GitifyNotificationUser, Hostname, Link } from '../types'; +import type { Notification, Repository } from '../typesGitHub'; import { getDeveloperSettingsURL } from './auth/utils'; import { openExternalLink } from './comms'; import { generateGitHubWebUrl } from './helpers'; @@ -37,7 +37,7 @@ export function openAccountProfile(account: Account) { openExternalLink(url.toString() as Link); } -export function openUserProfile(user: SubjectUser) { +export function openUserProfile(user: GitifyNotificationUser) { openExternalLink(user.html_url); } diff --git a/src/renderer/utils/notifications/filters/filter.test.ts b/src/renderer/utils/notifications/filters/filter.test.ts index 95f334869..d0610f162 100644 --- a/src/renderer/utils/notifications/filters/filter.test.ts +++ b/src/renderer/utils/notifications/filters/filter.test.ts @@ -169,8 +169,8 @@ describe('renderer/utils/notifications/filters/filter.ts', () => { }); it('should filter notifications by state when provided', async () => { - mockNotifications[0].subject.state = 'open'; - mockNotifications[1].subject.state = 'closed'; + mockNotifications[0].subject.state = 'OPEN'; + mockNotifications[1].subject.state = 'CLOSED'; const result = filterDetailedNotifications(mockNotifications, { ...mockSettings, filterStates: ['closed'], diff --git a/src/renderer/utils/notifications/filters/filter.ts b/src/renderer/utils/notifications/filters/filter.ts index 89ea66992..9ebb2812f 100644 --- a/src/renderer/utils/notifications/filters/filter.ts +++ b/src/renderer/utils/notifications/filters/filter.ts @@ -1,9 +1,9 @@ -import type { SettingsState } from '../../../types'; import type { - Notification, - StateType, - SubjectUser, -} from '../../../typesGitHub'; + GitifyNotificationState, + GitifyNotificationUser, + SettingsState, +} from '../../../types'; +import type { Notification } from '../../../typesGitHub'; import { BASE_SEARCH_QUALIFIERS, DETAILED_ONLY_SEARCH_QUALIFIERS, @@ -167,7 +167,7 @@ function passesStateFilter( } export function isStateFilteredOut( - state: StateType, + state: GitifyNotificationState, settings: SettingsState, ): boolean { const notification = { subject: { state: state } } as Notification; @@ -176,7 +176,7 @@ export function isStateFilteredOut( } export function isUserFilteredOut( - user: SubjectUser, + user: GitifyNotificationUser, settings: SettingsState, ): boolean { const notification = { subject: { user: user } } as Notification; diff --git a/src/renderer/utils/notifications/filters/state.test.ts b/src/renderer/utils/notifications/filters/state.test.ts index 78fce244b..0e2db8750 100644 --- a/src/renderer/utils/notifications/filters/state.test.ts +++ b/src/renderer/utils/notifications/filters/state.test.ts @@ -1,3 +1,4 @@ +import type { FilterStateType, GitifyNotificationState } from '../../../types'; import type { Notification } from '../../../typesGitHub'; import { stateFilter } from './state'; @@ -6,56 +7,52 @@ describe('renderer/utils/notifications/filters/state.ts', () => { jest.clearAllMocks(); }); - it('can filter by notification states', () => { - const mockPartialNotification = { - subject: { - state: 'open', - }, + describe('can filter by notification states', () => { + const mockNotification = { + subject: { state: 'OPEN' }, } as Partial as Notification; - // Open states - mockPartialNotification.subject.state = 'open'; - expect( - stateFilter.filterNotification(mockPartialNotification, 'open'), - ).toBe(true); - - mockPartialNotification.subject.state = 'reopened'; - expect( - stateFilter.filterNotification(mockPartialNotification, 'open'), - ).toBe(true); - - // Closed states - mockPartialNotification.subject.state = 'closed'; - expect( - stateFilter.filterNotification(mockPartialNotification, 'closed'), - ).toBe(true); - - mockPartialNotification.subject.state = 'completed'; - expect( - stateFilter.filterNotification(mockPartialNotification, 'closed'), - ).toBe(true); - - mockPartialNotification.subject.state = 'not_planned'; - expect( - stateFilter.filterNotification(mockPartialNotification, 'closed'), - ).toBe(true); - - // Merged states - mockPartialNotification.subject.state = 'merged'; - expect( - stateFilter.filterNotification(mockPartialNotification, 'merged'), - ).toBe(true); - - // Draft states - mockPartialNotification.subject.state = 'draft'; - expect( - stateFilter.filterNotification(mockPartialNotification, 'draft'), - ).toBe(true); - - // Other states - mockPartialNotification.subject.state = 'OUTDATED'; - expect( - stateFilter.filterNotification(mockPartialNotification, 'other'), - ).toBe(true); + const cases = { + OPEN: 'open', + REOPENED: 'open', + + CLOSED: 'closed', + COMPLETED: 'closed', + DUPLICATE: 'closed', + NOT_PLANNED: 'closed', + RESOLVED: 'closed', + + MERGED: 'merged', + DRAFT: 'draft', + + // Discussion-specific + ANSWERED: 'other', + OUTDATED: 'other', + + // Check suite / workflow states + ACTION_REQUIRED: 'other', + CANCELLED: 'other', + FAILURE: 'other', + IN_PROGRESS: 'other', + PENDING: 'other', + QUEUED: 'other', + REQUESTED: 'other', + SKIPPED: 'other', + STALE: 'other', + SUCCESS: 'other', + TIMED_OUT: 'other', + WAITING: 'other', + } satisfies Record; + + it.each( + Object.entries(cases) as Array< + [GitifyNotificationState, FilterStateType] + >, + )('filter notification with state %s as %s', (notificationState, expectedFilter) => { + mockNotification.subject.state = notificationState; + expect( + stateFilter.filterNotification(mockNotification, expectedFilter), + ).toBe(true); + }); }); }); diff --git a/src/renderer/utils/notifications/filters/state.ts b/src/renderer/utils/notifications/filters/state.ts index 9ed75f98a..2fc5451dc 100644 --- a/src/renderer/utils/notifications/filters/state.ts +++ b/src/renderer/utils/notifications/filters/state.ts @@ -1,6 +1,7 @@ import type { AccountNotifications, FilterStateType, + GitifyNotificationState, SettingsState, TypeDetails, } from '../../../types'; @@ -20,7 +21,7 @@ const STATE_TYPE_DETAILS: Record = { }, closed: { title: 'Closed', - description: 'Closed, completed or not planned', + description: 'Closed, completed, duplicate, resolved or not planned', }, other: { title: 'Other', @@ -63,28 +64,29 @@ export const stateFilter: Filter = { notification: Notification, stateType: FilterStateType, ): boolean { - const allOpenStates = ['open', 'reopened']; - const allClosedStates = ['closed', 'completed', 'not_planned']; - const allMergedStates = ['merged']; - const allDraftStates = ['draft']; - const allFilterableStates = [ - ...allOpenStates, - ...allClosedStates, - ...allMergedStates, - ...allDraftStates, - ]; - - switch (stateType) { - case 'open': - return allOpenStates.includes(notification.subject?.state); - case 'closed': - return allClosedStates.includes(notification.subject?.state); - case 'merged': - return allMergedStates.includes(notification.subject?.state); - case 'draft': - return allDraftStates.includes(notification.subject?.state); - default: - return !allFilterableStates.includes(notification.subject?.state); - } + const mapped = mapStateToFilter(notification.subject?.state); + return stateType === mapped; }, }; + +function mapStateToFilter( + state: GitifyNotificationState | null | undefined, +): FilterStateType { + switch (state) { + case 'OPEN': + case 'REOPENED': + return 'open'; + case 'CLOSED': + case 'COMPLETED': + case 'DUPLICATE': + case 'NOT_PLANNED': + case 'RESOLVED': + return 'closed'; + case 'MERGED': + return 'merged'; + case 'DRAFT': + return 'draft'; + default: + return 'other'; + } +} diff --git a/src/renderer/utils/notifications/handlers/checkSuite.test.ts b/src/renderer/utils/notifications/handlers/checkSuite.test.ts index 7687cb3f0..1a992c7c6 100644 --- a/src/renderer/utils/notifications/handlers/checkSuite.test.ts +++ b/src/renderer/utils/notifications/handlers/checkSuite.test.ts @@ -3,7 +3,12 @@ import { createPartialMockNotification, } from '../../../__mocks__/notifications-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; -import { IconColor } from '../../../types'; +import { + type GitifyCheckSuiteStatus, + IconColor, + type Link, +} from '../../../types'; +import type { Notification } from '../../../typesGitHub'; import { checkSuiteHandler, getCheckSuiteAttributes } from './checkSuite'; describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { @@ -20,8 +25,10 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { ); expect(result).toEqual({ - state: 'cancelled', + state: 'CANCELLED', user: null, + htmlUrl: + 'https://github.com/gitify-app/notifications-test/actions?query=workflow%3A%22Demo%22+is%3ACANCELLED+branch%3Amain', }); }); @@ -37,8 +44,10 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { ); expect(result).toEqual({ - state: 'failure', + state: 'FAILURE', user: null, + htmlUrl: + 'https://github.com/gitify-app/notifications-test/actions?query=workflow%3A%22Demo%22+is%3AFAILURE+branch%3Amain', }); }); @@ -54,8 +63,10 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { ); expect(result).toEqual({ - state: 'failure', + state: 'FAILURE', user: null, + htmlUrl: + 'https://github.com/gitify-app/notifications-test/actions?query=workflow%3A%22Demo%22+is%3AFAILURE+branch%3Amain', }); }); @@ -71,8 +82,10 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { ); expect(result).toEqual({ - state: 'failure', + state: 'FAILURE', user: null, + htmlUrl: + 'https://github.com/gitify-app/notifications-test/actions?query=workflow%3A%22Demo%22+is%3AFAILURE+branch%3Amain', }); }); @@ -88,8 +101,10 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { ); expect(result).toEqual({ - state: 'skipped', + state: 'SKIPPED', user: null, + htmlUrl: + 'https://github.com/gitify-app/notifications-test/actions?query=workflow%3A%22Demo%22+is%3ASKIPPED+branch%3Amain', }); }); @@ -105,8 +120,10 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { ); expect(result).toEqual({ - state: 'success', + state: 'SUCCESS', user: null, + htmlUrl: + 'https://github.com/gitify-app/notifications-test/actions?query=workflow%3A%22Demo%22+is%3ASUCCESS+branch%3Amain', }); }); @@ -139,80 +156,76 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { }); }); - it('iconType', () => { - expect( - checkSuiteHandler.iconType( - createMockSubject({ type: 'CheckSuite', state: null }), - ).displayName, - ).toBe('RocketIcon'); - - expect( - checkSuiteHandler.iconType( - createMockSubject({ - type: 'CheckSuite', - state: 'cancelled', - }), - ).displayName, - ).toBe('StopIcon'); - - expect( - checkSuiteHandler.iconType( - createMockSubject({ - type: 'CheckSuite', - state: 'failure', - }), - ).displayName, - ).toBe('XIcon'); - - expect( - checkSuiteHandler.iconType( - createMockSubject({ - type: 'CheckSuite', - state: 'skipped', - }), - ).displayName, - ).toBe('SkipIcon'); - - expect( - checkSuiteHandler.iconType( - createMockSubject({ - type: 'CheckSuite', - state: 'success', - }), - ).displayName, - ).toBe('CheckIcon'); + describe('iconType', () => { + const cases = { + ACTION_REQUIRED: 'RocketIcon', + CANCELLED: 'StopIcon', + COMPLETED: 'RocketIcon', + FAILURE: 'XIcon', + IN_PROGRESS: 'RocketIcon', + PENDING: 'RocketIcon', + QUEUED: 'RocketIcon', + REQUESTED: 'RocketIcon', + SKIPPED: 'SkipIcon', + STALE: 'RocketIcon', + SUCCESS: 'CheckIcon', + TIMED_OUT: 'RocketIcon', + WAITING: 'RocketIcon', + } satisfies Record; + + it.each( + Object.entries(cases) as Array<[GitifyCheckSuiteStatus, IconColor]>, + )('iconType for check suite with status %s', (checkSuiteStatus, checkSuiteIconType) => { + expect( + checkSuiteHandler.iconType( + createMockSubject({ type: 'CheckSuite', state: checkSuiteStatus }), + ).displayName, + ).toBe(checkSuiteIconType); + }); }); - it('iconColor', () => { - expect( - checkSuiteHandler.iconColor( - createMockSubject({ type: 'CheckSuite', state: 'success' }), - ), - ).toBe(IconColor.GREEN); - - expect( - checkSuiteHandler.iconColor( - createMockSubject({ type: 'CheckSuite', state: 'failure' }), - ), - ).toBe(IconColor.RED); - - expect( - checkSuiteHandler.iconColor( - createMockSubject({ type: 'CheckSuite', state: 'cancelled' }), - ), - ).toBe(IconColor.GRAY); + describe('iconColor', () => { + const cases = { + ACTION_REQUIRED: IconColor.GRAY, + CANCELLED: IconColor.GRAY, + COMPLETED: IconColor.GRAY, + FAILURE: IconColor.RED, + IN_PROGRESS: IconColor.GRAY, + PENDING: IconColor.GRAY, + QUEUED: IconColor.GRAY, + REQUESTED: IconColor.GRAY, + SKIPPED: IconColor.GRAY, + STALE: IconColor.GRAY, + SUCCESS: IconColor.GREEN, + TIMED_OUT: IconColor.GRAY, + WAITING: IconColor.GRAY, + } satisfies Record; + + it.each( + Object.entries(cases) as Array<[GitifyCheckSuiteStatus, IconColor]>, + )('iconColor for check suite with status %s', (checkSuiteStatus, checkSuiteIconColor) => { + expect( + checkSuiteHandler.iconColor( + createMockSubject({ type: 'CheckSuite', state: checkSuiteStatus }), + ), + ).toBe(checkSuiteIconColor); + }); + }); - expect( - checkSuiteHandler.iconColor( - createMockSubject({ type: 'CheckSuite', state: 'skipped' }), - ), - ).toBe(IconColor.GRAY); + it('defaultUrl', () => { + const mockHtmlUrl = + 'https://github.com/gitify-app/notifications-test' as Link; expect( - checkSuiteHandler.iconColor( - createMockSubject({ type: 'CheckSuite', state: null }), - ), - ).toBe(IconColor.GRAY); + checkSuiteHandler.defaultUrl({ + subject: { + title: 'Some notification', + }, + repository: { + html_url: mockHtmlUrl, + }, + } as Notification), + ).toEqual(`${mockHtmlUrl}/actions`); }); describe('getCheckSuiteState', () => { @@ -227,7 +240,7 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { expect(result).toEqual({ workflowName: 'Demo', attemptNumber: null, - status: 'cancelled', + status: 'CANCELLED', statusDisplayName: 'cancelled', branchName: 'feature/foo', }); @@ -244,7 +257,7 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { expect(result).toEqual({ workflowName: 'Demo', attemptNumber: null, - status: 'failure', + status: 'FAILURE', statusDisplayName: 'failed', branchName: 'main', }); @@ -261,7 +274,7 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { expect(result).toEqual({ workflowName: 'Demo', attemptNumber: 3, - status: 'failure', + status: 'FAILURE', statusDisplayName: 'failed', branchName: 'main', }); @@ -278,7 +291,7 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { expect(result).toEqual({ workflowName: 'Demo', attemptNumber: null, - status: 'skipped', + status: 'SKIPPED', statusDisplayName: 'skipped', branchName: 'main', }); @@ -295,7 +308,7 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { expect(result).toEqual({ workflowName: 'Demo', attemptNumber: null, - status: 'success', + status: 'SUCCESS', statusDisplayName: 'succeeded', branchName: 'main', }); diff --git a/src/renderer/utils/notifications/handlers/checkSuite.ts b/src/renderer/utils/notifications/handlers/checkSuite.ts index dbeac0ae8..1985c50d8 100644 --- a/src/renderer/utils/notifications/handlers/checkSuite.ts +++ b/src/renderer/utils/notifications/handlers/checkSuite.ts @@ -9,17 +9,25 @@ import { XIcon, } from '@primer/octicons-react'; -import type { SettingsState } from '../../../types'; -import { IconColor } from '../../../types'; -import type { - CheckSuiteAttributes, - CheckSuiteStatus, - GitifySubject, - Notification, - Subject, -} from '../../../typesGitHub'; +import { + type GitifyCheckSuiteStatus, + type GitifySubject, + IconColor, + type Link, + type SettingsState, +} from '../../../types'; +import type { Notification, Subject } from '../../../typesGitHub'; +import { actionsURL } from '../../helpers'; import { DefaultHandler, defaultHandler } from './default'; +export interface CheckSuiteAttributes { + workflowName: string; + attemptNumber?: number; + statusDisplayName: string; + status: GitifyCheckSuiteStatus | null; + branchName: string; +} + class CheckSuiteHandler extends DefaultHandler { readonly type = 'CheckSuite'; @@ -33,6 +41,7 @@ class CheckSuiteHandler extends DefaultHandler { return { state: state, user: null, + htmlUrl: getCheckSuiteUrl(notification), }; } @@ -40,14 +49,14 @@ class CheckSuiteHandler extends DefaultHandler { } iconType(subject: Subject): FC | null { - switch (subject.state) { - case 'cancelled': + switch (subject.state as GitifyCheckSuiteStatus) { + case 'CANCELLED': return StopIcon; - case 'failure': + case 'FAILURE': return XIcon; - case 'skipped': + case 'SKIPPED': return SkipIcon; - case 'success': + case 'SUCCESS': return CheckIcon; default: return RocketIcon; @@ -55,15 +64,19 @@ class CheckSuiteHandler extends DefaultHandler { } iconColor(subject: Subject): IconColor { - switch (subject.state) { - case 'success': + switch (subject.state as GitifyCheckSuiteStatus) { + case 'SUCCESS': return IconColor.GREEN; - case 'failure': + case 'FAILURE': return IconColor.RED; default: return defaultHandler.iconColor(subject); } } + + defaultUrl(notification: Notification): Link { + return getCheckSuiteUrl(notification); + } } export const checkSuiteHandler = new CheckSuiteHandler(); @@ -96,18 +109,42 @@ export function getCheckSuiteAttributes( }; } -function getCheckSuiteStatus(statusDisplayName: string): CheckSuiteStatus { +function getCheckSuiteStatus( + statusDisplayName: string, +): GitifyCheckSuiteStatus { switch (statusDisplayName) { case 'cancelled': - return 'cancelled'; + return 'CANCELLED'; case 'failed': case 'failed at startup': - return 'failure'; + return 'FAILURE'; case 'skipped': - return 'skipped'; + return 'SKIPPED'; case 'succeeded': - return 'success'; + return 'SUCCESS'; default: return null; } } + +export function getCheckSuiteUrl(notification: Notification): Link { + const filters = []; + + const checkSuiteAttributes = getCheckSuiteAttributes(notification); + + if (checkSuiteAttributes?.workflowName) { + filters.push( + `workflow:"${checkSuiteAttributes.workflowName.replaceAll(' ', '+')}"`, + ); + } + + if (checkSuiteAttributes?.status) { + filters.push(`is:${checkSuiteAttributes.status}`); + } + + if (checkSuiteAttributes?.branchName) { + filters.push(`branch:${checkSuiteAttributes.branchName}`); + } + + return actionsURL(notification.repository.html_url, filters); +} diff --git a/src/renderer/utils/notifications/handlers/commit.test.ts b/src/renderer/utils/notifications/handlers/commit.test.ts index d1a54542e..a846bb150 100644 --- a/src/renderer/utils/notifications/handlers/commit.test.ts +++ b/src/renderer/utils/notifications/handlers/commit.test.ts @@ -8,6 +8,7 @@ import { import { mockSettings } from '../../../__mocks__/state-mocks'; import { createPartialMockUser } from '../../../__mocks__/user-mocks'; import type { Link } from '../../../types'; +import type { Notification } from '../../../typesGitHub'; import { commitHandler } from './commit'; describe('renderer/utils/notifications/handlers/commit.ts', () => { @@ -102,4 +103,17 @@ describe('renderer/utils/notifications/handlers/commit.ts', () => { commitHandler.iconType(createMockSubject({ type: 'Commit' })).displayName, ).toBe('GitCommitIcon'); }); + + it('defaultUrl', () => { + const mockHtmlUrl = + 'https://github.com/gitify-app/notifications-test' as Link; + + expect( + commitHandler.defaultUrl({ + repository: { + html_url: mockHtmlUrl, + }, + } as Notification), + ).toEqual(mockHtmlUrl); + }); }); diff --git a/src/renderer/utils/notifications/handlers/commit.ts b/src/renderer/utils/notifications/handlers/commit.ts index 882134f03..cc13f5bcd 100644 --- a/src/renderer/utils/notifications/handlers/commit.ts +++ b/src/renderer/utils/notifications/handlers/commit.ts @@ -3,18 +3,16 @@ import type { FC } from 'react'; import type { OcticonProps } from '@primer/octicons-react'; import { GitCommitIcon } from '@primer/octicons-react'; -import type { SettingsState } from '../../../types'; import type { + GitifyNotificationState, GitifySubject, - Notification, - StateType, - Subject, - User, -} from '../../../typesGitHub'; + SettingsState, +} from '../../../types'; +import type { Notification, Subject, User } from '../../../typesGitHub'; import { getCommit, getCommitComment } from '../../api/client'; import { isStateFilteredOut } from '../filters/filter'; import { DefaultHandler } from './default'; -import { getSubjectUser } from './utils'; +import { getNotificationAuthor } from './utils'; class CommitHandler extends DefaultHandler { readonly type = 'Commit'; @@ -23,7 +21,7 @@ class CommitHandler extends DefaultHandler { notification: Notification, settings: SettingsState, ): Promise { - const commitState: StateType = null; // Commit notifications are stateless + const commitState: GitifyNotificationState = null; // Commit notifications are stateless // Return early if this notification would be hidden by filters if (isStateFilteredOut(commitState, settings)) { @@ -51,7 +49,7 @@ class CommitHandler extends DefaultHandler { return { state: commitState, - user: getSubjectUser([user]), + user: getNotificationAuthor([user]), }; } diff --git a/src/renderer/utils/notifications/handlers/default.test.ts b/src/renderer/utils/notifications/handlers/default.test.ts index 3d1abebc8..bc7371727 100644 --- a/src/renderer/utils/notifications/handlers/default.test.ts +++ b/src/renderer/utils/notifications/handlers/default.test.ts @@ -3,8 +3,12 @@ import { createPartialMockNotification, } from '../../../__mocks__/notifications-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; -import { IconColor } from '../../../types'; -import type { StateType } from '../../../typesGitHub'; +import { + type GitifyNotificationState, + IconColor, + type Link, +} from '../../../types'; +import type { Notification } from '../../../typesGitHub'; import { defaultHandler } from './default'; describe('renderer/utils/notifications/handlers/default.ts', () => { @@ -33,8 +37,8 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { describe('iconColor', () => { it('returns GRAY for any state (fallback behavior)', () => { - const states: Array = [ - 'unknown' as StateType, + const states: Array = [ + 'unknown' as GitifyNotificationState, null, undefined, ]; @@ -51,7 +55,7 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { const notification = createPartialMockNotification({ title: 'Sample', type: 'PullRequest', - state: 'open', + state: 'OPEN', }); expect(defaultHandler.formattedNotificationType(notification)).toBe( @@ -77,7 +81,7 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { const notification = createPartialMockNotification({ title: 'Sample', type: 'Issue', - state: 'open', + state: 'OPEN', }); notification.subject.number = 42; expect(defaultHandler.formattedNotificationNumber(notification)).toBe( @@ -89,7 +93,7 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { const notification = createPartialMockNotification({ title: 'Sample', type: 'Issue', - state: 'open', + state: 'OPEN', }); expect(defaultHandler.formattedNotificationNumber(notification)).toBe(''); }); @@ -100,7 +104,7 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { const notification = createPartialMockNotification({ title: 'Fix bug', type: 'Issue', - state: 'open', + state: 'OPEN', }); notification.subject.number = 101; expect(defaultHandler.formattedNotificationTitle(notification)).toBe( @@ -112,11 +116,24 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { const notification = createPartialMockNotification({ title: 'Improve docs', type: 'Issue', - state: 'open', + state: 'OPEN', }); expect(defaultHandler.formattedNotificationTitle(notification)).toBe( 'Improve docs', ); }); }); + + it('defaultUrl', () => { + const mockHtmlUrl = + 'https://github.com/gitify-app/notifications-test' as Link; + + expect( + defaultHandler.defaultUrl({ + repository: { + html_url: mockHtmlUrl, + }, + } as Notification), + ).toEqual(mockHtmlUrl); + }); }); diff --git a/src/renderer/utils/notifications/handlers/default.ts b/src/renderer/utils/notifications/handlers/default.ts index 4b8bba274..8c674b6a3 100644 --- a/src/renderer/utils/notifications/handlers/default.ts +++ b/src/renderer/utils/notifications/handlers/default.ts @@ -3,14 +3,9 @@ import type { FC } from 'react'; import type { OcticonProps } from '@primer/octicons-react'; import { QuestionIcon } from '@primer/octicons-react'; -import type { SettingsState } from '../../../types'; +import type { GitifySubject, Link, SettingsState } from '../../../types'; import { IconColor } from '../../../types'; -import type { - GitifySubject, - Notification, - Subject, - SubjectType, -} from '../../../typesGitHub'; +import type { Notification, Subject, SubjectType } from '../../../typesGitHub'; import type { NotificationTypeHandler } from './types'; import { formatForDisplay } from './utils'; @@ -38,11 +33,13 @@ export class DefaultHandler implements NotificationTypeHandler { notification.subject.type, ]); } + formattedNotificationNumber(notification: Notification): string { return notification.subject?.number ? `#${notification.subject.number}` : ''; } + formattedNotificationTitle(notification: Notification): string { let title = notification.subject.title; @@ -51,6 +48,10 @@ export class DefaultHandler implements NotificationTypeHandler { } return title; } + + defaultUrl(notification: Notification): Link { + return notification.repository.html_url; + } } export const defaultHandler = new DefaultHandler(); diff --git a/src/renderer/utils/notifications/handlers/discussion.test.ts b/src/renderer/utils/notifications/handlers/discussion.test.ts index 0b54c684e..69ab51b9c 100644 --- a/src/renderer/utils/notifications/handlers/discussion.test.ts +++ b/src/renderer/utils/notifications/handlers/discussion.test.ts @@ -6,42 +6,36 @@ import { createPartialMockNotification, } from '../../../__mocks__/notifications-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; -import { IconColor, type Link } from '../../../types'; -import type { Owner, Repository } from '../../../typesGitHub'; +import { createPartialMockUser } from '../../../__mocks__/user-mocks'; import { - type AuthorFieldsFragment, - type Discussion, + type GitifyDiscussionState, + type GitifySubject, + IconColor, + type Link, +} from '../../../types'; +import type { Notification } from '../../../typesGitHub'; +import type { DiscussionStateReason, + FetchDiscussionByNumberQuery, } from '../../api/graphql/generated/graphql'; import { discussionHandler } from './discussion'; -const mockDiscussionAuthor: AuthorFieldsFragment = { - login: 'discussion-author', - url: 'https://github.com/discussion-author' as Link, - avatar_url: 'https://avatars.githubusercontent.com/u/123456789?v=4' as Link, - type: 'User', -}; +type DiscussionResponse = + FetchDiscussionByNumberQuery['repository']['discussion']; + +const mockAuthor = createPartialMockUser('discussion-author'); +const mockCommenter = createPartialMockUser('discussion-commenter'); +const mockReplier = createPartialMockUser('discussion-replier'); describe('renderer/utils/notifications/handlers/discussion.ts', () => { describe('enrich', () => { - const partialOwner: Partial = { - login: 'gitify-app', - }; - - const partialRepository: Partial = { - full_name: 'gitify-app/notifications-test', - owner: partialOwner as Owner, - }; - const mockNotification = createPartialMockNotification({ title: 'This is a mock discussion', type: 'Discussion', url: 'https://api.github.com/repos/gitify-app/notifications-test/discussions/123' as Link, + latest_comment_url: null, }); mockNotification.updated_at = '2024-01-01T00:00:00Z'; - mockNotification.repository = { - ...(partialRepository as Repository), - }; beforeEach(() => { // axios will default to using the XHR adapter which can't be intercepted @@ -49,13 +43,15 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { axios.defaults.adapter = 'http'; }); - it('answered discussion state', async () => { + it('answered discussion state - no stateReason', async () => { + const mockDiscussion = mockDiscussionResponseNode({ isAnswered: true }); + nock('https://api.github.com') .post('/graphql') .reply(200, { data: { repository: { - discussion: mockDiscussionNode(null, true), + discussion: mockDiscussion, }, }, }); @@ -69,26 +65,27 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { number: 123, state: 'ANSWERED', user: { - login: mockDiscussionAuthor.login, - html_url: mockDiscussionAuthor.url, - avatar_url: mockDiscussionAuthor.avatar_url, - type: mockDiscussionAuthor.type, + login: mockAuthor.login, + html_url: mockAuthor.html_url, + avatar_url: mockAuthor.avatar_url, + type: mockAuthor.type, }, comments: 0, labels: [], - }); + htmlUrl: + 'https://github.com/gitify-app/notifications-test/discussions/123', + } as GitifySubject); }); - it('duplicate discussion state', async () => { + it('open / unanswered discussion - no stateReason', async () => { + const mockDiscussion = mockDiscussionResponseNode({ isAnswered: false }); + nock('https://api.github.com') .post('/graphql') .reply(200, { data: { repository: { - discussion: mockDiscussionNode( - DiscussionStateReason.Duplicate, - false, - ), + discussion: mockDiscussion, }, }, }); @@ -100,25 +97,32 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { expect(result).toEqual({ number: 123, - state: 'DUPLICATE', + state: 'OPEN', user: { - login: mockDiscussionAuthor.login, - html_url: mockDiscussionAuthor.url, - avatar_url: mockDiscussionAuthor.avatar_url, - type: mockDiscussionAuthor.type, + login: mockAuthor.login, + html_url: mockAuthor.html_url, + avatar_url: mockAuthor.avatar_url, + type: mockAuthor.type, }, comments: 0, labels: [], - }); + htmlUrl: + 'https://github.com/gitify-app/notifications-test/discussions/123', + } as GitifySubject); }); - it('open discussion state', async () => { + it('discussion with stateReason - stateReason always takes precedence', async () => { + const mockDiscussion = mockDiscussionResponseNode({ + isAnswered: true, + stateReason: 'DUPLICATE', + }); + nock('https://api.github.com') .post('/graphql') .reply(200, { data: { repository: { - discussion: mockDiscussionNode(null, false), + discussion: mockDiscussion, }, }, }); @@ -130,28 +134,36 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { expect(result).toEqual({ number: 123, - state: 'OPEN', + state: 'DUPLICATE', user: { - login: mockDiscussionAuthor.login, - html_url: mockDiscussionAuthor.url, - avatar_url: mockDiscussionAuthor.avatar_url, - type: mockDiscussionAuthor.type, + login: mockAuthor.login, + html_url: mockAuthor.html_url, + avatar_url: mockAuthor.avatar_url, + type: mockAuthor.type, }, comments: 0, labels: [], - }); + htmlUrl: + 'https://github.com/gitify-app/notifications-test/discussions/123', + } as GitifySubject); }); - it('outdated discussion state', async () => { + it('discussion with labels', async () => { + const mockDiscussion = mockDiscussionResponseNode({ isAnswered: true }); + mockDiscussion.labels = { + nodes: [ + { + name: 'enhancement', + }, + ], + }; + nock('https://api.github.com') .post('/graphql') .reply(200, { data: { repository: { - discussion: mockDiscussionNode( - DiscussionStateReason.Outdated, - false, - ), + discussion: mockDiscussion, }, }, }); @@ -163,61 +175,43 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { expect(result).toEqual({ number: 123, - state: 'OUTDATED', + state: 'ANSWERED', user: { - login: mockDiscussionAuthor.login, - html_url: mockDiscussionAuthor.url, - avatar_url: mockDiscussionAuthor.avatar_url, - type: mockDiscussionAuthor.type, + login: mockAuthor.login, + html_url: mockAuthor.html_url, + avatar_url: mockAuthor.avatar_url, + type: mockAuthor.type, }, comments: 0, - labels: [], - }); + labels: ['enhancement'], + htmlUrl: + 'https://github.com/gitify-app/notifications-test/discussions/123', + } as GitifySubject); }); - it('reopened discussion state', async () => { - nock('https://api.github.com') - .post('/graphql') - .reply(200, { - data: { - repository: { - discussion: mockDiscussionNode( - DiscussionStateReason.Reopened, - false, - ), + it('discussion with comments', async () => { + const mockDiscussion = mockDiscussionResponseNode({ isAnswered: true }); + mockDiscussion.comments = { + totalCount: 1, + nodes: [ + { + author: mockCommenter, + createdAt: '2024-02-01T00:00:00Z', + url: 'https://github.com/gitify-app/notifications-test/discussions/123#discussioncomment-1234', + replies: { + totalCount: 0, + nodes: [], }, }, - }); - - const result = await discussionHandler.enrich( - mockNotification, - mockSettings, - ); - - expect(result).toEqual({ - number: 123, - state: 'REOPENED', - user: { - login: mockDiscussionAuthor.login, - html_url: mockDiscussionAuthor.url, - avatar_url: mockDiscussionAuthor.avatar_url, - type: mockDiscussionAuthor.type, - }, - comments: 0, - labels: [], - }); - }); + ], + }; - it('resolved discussion state', async () => { nock('https://api.github.com') .post('/graphql') .reply(200, { data: { repository: { - discussion: mockDiscussionNode( - DiscussionStateReason.Resolved, - true, - ), + discussion: mockDiscussion, }, }, }); @@ -229,35 +223,49 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { expect(result).toEqual({ number: 123, - state: 'RESOLVED', + state: 'ANSWERED', user: { - login: mockDiscussionAuthor.login, - html_url: mockDiscussionAuthor.url, - avatar_url: mockDiscussionAuthor.avatar_url, - type: mockDiscussionAuthor.type, + login: mockCommenter.login, + html_url: mockCommenter.html_url, + avatar_url: mockCommenter.avatar_url, + type: mockCommenter.type, }, - comments: 0, + comments: 1, labels: [], - }); + htmlUrl: + 'https://github.com/gitify-app/notifications-test/discussions/123#discussioncomment-1234', + } as GitifySubject); }); - it('discussion with labels', async () => { - const mockDiscussion = mockDiscussionNode(null, true); - mockDiscussion.labels = { + it('discussion with comments and replies', async () => { + const mockDiscussion = mockDiscussionResponseNode({ isAnswered: true }); + mockDiscussion.comments = { + totalCount: 1, nodes: [ { - name: 'enhancement', + author: mockCommenter, + createdAt: '2024-01-01T00:00:00Z', + url: 'https://github.com/gitify-app/notifications-test/discussions/123#discussioncomment-1234', + replies: { + totalCount: 1, + nodes: [ + { + author: mockReplier, + createdAt: '2024-01-01T00:00:00Z', + url: 'https://github.com/gitify-app/notifications-test/discussions/123#discussioncomment-6789', + }, + ], + }, }, ], - } as Partial['labels']; + }; + nock('https://api.github.com') .post('/graphql') .reply(200, { data: { repository: { - discussion: { - ...mockDiscussion, - }, + discussion: mockDiscussion, }, }, }); @@ -271,109 +279,91 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { number: 123, state: 'ANSWERED', user: { - login: mockDiscussionAuthor.login, - html_url: mockDiscussionAuthor.url, - avatar_url: mockDiscussionAuthor.avatar_url, - type: mockDiscussionAuthor.type, + login: mockReplier.login, + html_url: mockReplier.html_url, + avatar_url: mockReplier.avatar_url, + type: mockReplier.type, }, - comments: 0, - labels: ['enhancement'], - }); + comments: 1, + labels: [], + htmlUrl: + 'https://github.com/gitify-app/notifications-test/discussions/123#discussioncomment-6789', + } as GitifySubject); }); + }); - it('early return if discussion state filtered', async () => { - nock('https://api.github.com') - .post('/graphql') - .reply(200, { - data: { - repository: { - discussion: mockDiscussionNode(null, false), - }, - }, - }); - - const result = await discussionHandler.enrich(mockNotification, { - ...mockSettings, - filterStates: ['closed'], - }); - - expect(result).toEqual(null); + describe('iconType', () => { + const cases = { + ANSWERED: 'CommentDiscussionIcon', + DUPLICATE: 'DiscussionDuplicateIcon', + OPEN: 'CommentDiscussionIcon', + OUTDATED: 'DiscussionOutdatedIcon', + REOPENED: 'CommentDiscussionIcon', + RESOLVED: 'DiscussionClosedIcon', + } satisfies Record; + + it.each( + Object.entries(cases) as Array<[GitifyDiscussionState, IconColor]>, + )('iconType for discussion with state %s', (discussionState, discussionIconType) => { + expect( + discussionHandler.iconType( + createMockSubject({ type: 'Discussion', state: discussionState }), + ).displayName, + ).toBe(discussionIconType); }); }); - it('iconType', () => { - expect( - discussionHandler.iconType(createMockSubject({ type: 'Discussion' })) - .displayName, - ).toBe('CommentDiscussionIcon'); - - expect( - discussionHandler.iconType( - createMockSubject({ type: 'Discussion', state: 'DUPLICATE' }), - ).displayName, - ).toBe('DiscussionDuplicateIcon'); - - expect( - discussionHandler.iconType( - createMockSubject({ type: 'Discussion', state: 'OUTDATED' }), - ).displayName, - ).toBe('DiscussionOutdatedIcon'); - - expect( - discussionHandler.iconType( - createMockSubject({ type: 'Discussion', state: 'RESOLVED' }), - ).displayName, - ).toBe('DiscussionClosedIcon'); + describe('iconColor', () => { + const cases = { + ANSWERED: IconColor.GREEN, + DUPLICATE: IconColor.GRAY, + OPEN: IconColor.GRAY, + OUTDATED: IconColor.GRAY, + REOPENED: IconColor.GRAY, + RESOLVED: IconColor.PURPLE, + } satisfies Record; + + it.each( + Object.entries(cases) as Array<[GitifyDiscussionState, IconColor]>, + )('iconColor for discussion with state %s', (discussionState, discussionIconColor) => { + expect( + discussionHandler.iconColor( + createMockSubject({ type: 'Discussion', state: discussionState }), + ), + ).toBe(discussionIconColor); + }); }); - it('iconColor', () => { - expect( - discussionHandler.iconColor( - createMockSubject({ type: 'Discussion', state: 'ANSWERED' }), - ), - ).toBe(IconColor.GREEN); - - expect( - discussionHandler.iconColor( - createMockSubject({ type: 'Discussion', state: 'RESOLVED' }), - ), - ).toBe(IconColor.PURPLE); + it('defaultUrl', () => { + const mockHtmlUrl = + 'https://github.com/gitify-app/notifications-test' as Link; expect( - discussionHandler.iconColor( - createMockSubject({ type: 'Discussion', state: 'DUPLICATE' }), - ), - ).toBe(IconColor.GRAY); - - expect( - discussionHandler.iconColor( - createMockSubject({ type: 'Discussion', state: 'OUTDATED' }), - ), - ).toBe(IconColor.GRAY); - - expect( - discussionHandler.iconColor( - createMockSubject({ type: 'Discussion', state: 'OPEN' }), - ), - ).toBe(IconColor.GRAY); + discussionHandler.defaultUrl({ + repository: { + html_url: mockHtmlUrl, + }, + } as Notification), + ).toEqual(`${mockHtmlUrl}/discussions`); }); }); -function mockDiscussionNode( - state: DiscussionStateReason, - isAnswered: boolean, -): Partial { +function mockDiscussionResponseNode(mocks: { + stateReason?: DiscussionStateReason; + isAnswered: boolean; +}): DiscussionResponse { return { + __typename: 'Discussion', number: 123, title: 'This is a mock discussion', - url: 'https://github.com/gitify-app/notifications-test/discussions/1' as Link, - stateReason: state, - isAnswered: isAnswered, - author: mockDiscussionAuthor, + url: 'https://github.com/gitify-app/notifications-test/discussions/123' as Link, + stateReason: mocks.stateReason, + isAnswered: mocks.isAnswered, + author: mockAuthor, comments: { nodes: [], totalCount: 0, }, labels: null, - } as unknown as Partial; + }; } diff --git a/src/renderer/utils/notifications/handlers/discussion.ts b/src/renderer/utils/notifications/handlers/discussion.ts index eee06cbcf..30a993371 100644 --- a/src/renderer/utils/notifications/handlers/discussion.ts +++ b/src/renderer/utils/notifications/handlers/discussion.ts @@ -10,22 +10,21 @@ import { import { differenceInMilliseconds } from 'date-fns'; -import type { SettingsState } from '../../../types'; -import { IconColor } from '../../../types'; -import type { - DiscussionStateType, - GitifySubject, - Notification, - Subject, - SubjectUser, -} from '../../../typesGitHub'; +import { + type GitifyDiscussionState, + type GitifySubject, + IconColor, + type Link, + type SettingsState, +} from '../../../types'; +import type { Notification, Subject } from '../../../typesGitHub'; import { fetchDiscussionByNumber } from '../../api/client'; import type { CommentFieldsFragment, FetchDiscussionByNumberQuery, } from '../../api/graphql/generated/graphql'; -import { isStateFilteredOut } from '../filters/filter'; import { DefaultHandler, defaultHandler } from './default'; +import { getNotificationAuthor } from './utils'; type DiscussionComment = NonNullable< NonNullable< @@ -45,16 +44,12 @@ class DiscussionHandler extends DefaultHandler { async enrich( notification: Notification, - settings: SettingsState, + _settings: SettingsState, ): Promise { const response = await fetchDiscussionByNumber(notification); const discussion = response.data.repository?.discussion; - if (!discussion) { - return null; - } - - let discussionState: DiscussionStateType = 'OPEN'; + let discussionState: GitifyDiscussionState = 'OPEN'; if (discussion.isAnswered) { discussionState = 'ANSWERED'; @@ -64,43 +59,26 @@ class DiscussionHandler extends DefaultHandler { discussionState = discussion.stateReason; } - // Return early if this notification would be hidden by filters - if (isStateFilteredOut(discussionState, settings)) { - return null; - } - const latestDiscussionComment = getClosestDiscussionCommentOrReply( notification, discussion.comments.nodes, ); - let discussionUser: SubjectUser = { - login: discussion.author.login, - html_url: discussion.author.url, - avatar_url: discussion.author.avatar_url, - type: discussion.author.type, - }; - - if (latestDiscussionComment) { - discussionUser = { - login: latestDiscussionComment.author.login, - html_url: latestDiscussionComment.author.url, - avatar_url: latestDiscussionComment.author.avatar_url, - type: latestDiscussionComment.author.type, - }; - } - return { number: discussion.number, state: discussionState, - user: discussionUser, + user: getNotificationAuthor([ + latestDiscussionComment?.author, + discussion.author, + ]), comments: discussion.comments.totalCount, labels: discussion.labels?.nodes.map((label) => label.name) ?? [], + htmlUrl: latestDiscussionComment?.url ?? discussion.url, }; } iconType(subject: Subject): FC | null { - switch (subject.state) { + switch (subject.state as GitifyDiscussionState) { case 'DUPLICATE': return DiscussionDuplicateIcon; case 'OUTDATED': @@ -122,6 +100,12 @@ class DiscussionHandler extends DefaultHandler { return defaultHandler.iconColor(subject); } } + + defaultUrl(notification: Notification): Link { + const url = new URL(notification.repository.html_url); + url.pathname += '/discussions'; + return url.href as Link; + } } export const discussionHandler = new DiscussionHandler(); @@ -137,8 +121,8 @@ export function getClosestDiscussionCommentOrReply( const targetTimestamp = notification.updated_at; const allCommentsAndReplies = comments.flatMap((comment) => [ - comment, ...comment.replies.nodes, + comment, ]); // Find the closest match using the target timestamp diff --git a/src/renderer/utils/notifications/handlers/index.test.ts b/src/renderer/utils/notifications/handlers/index.test.ts index f06a83e99..c30155a9c 100644 --- a/src/renderer/utils/notifications/handlers/index.test.ts +++ b/src/renderer/utils/notifications/handlers/index.test.ts @@ -11,28 +11,26 @@ import { releaseHandler } from './release'; import { repositoryDependabotAlertsThreadHandler } from './repositoryDependabotAlertsThread'; import { repositoryInvitationHandler } from './repositoryInvitation'; import { repositoryVulnerabilityAlertHandler } from './repositoryVulnerabilityAlert'; +import type { NotificationTypeHandler } from './types'; import { workflowRunHandler } from './workflowRun'; describe('renderer/utils/notifications/handlers/index.ts', () => { describe('createNotificationHandler', () => { - const cases: Array<[SubjectType, object]> = [ - ['CheckSuite', checkSuiteHandler], - ['Commit', commitHandler], - ['Discussion', discussionHandler], - ['Issue', issueHandler], - ['PullRequest', pullRequestHandler], - ['Release', releaseHandler], - [ - 'RepositoryDependabotAlertsThread', - repositoryDependabotAlertsThreadHandler, - ], - ['RepositoryInvitation', repositoryInvitationHandler], - ['RepositoryVulnerabilityAlert', repositoryVulnerabilityAlertHandler], - ['WorkflowRun', workflowRunHandler], - ]; + const cases = { + CheckSuite: checkSuiteHandler, + Commit: commitHandler, + Discussion: discussionHandler, + Issue: issueHandler, + PullRequest: pullRequestHandler, + Release: releaseHandler, + RepositoryDependabotAlertsThread: repositoryDependabotAlertsThreadHandler, + RepositoryInvitation: repositoryInvitationHandler, + RepositoryVulnerabilityAlert: repositoryVulnerabilityAlertHandler, + WorkflowRun: workflowRunHandler, + } satisfies Record; it.each( - cases, + Object.entries(cases) as Array<[SubjectType, NotificationTypeHandler]>, )('returns expected handler instance for %s', (type, expected) => { const notification = createPartialMockNotification({ type }); const handler = createNotificationHandler(notification); diff --git a/src/renderer/utils/notifications/handlers/issue.test.ts b/src/renderer/utils/notifications/handlers/issue.test.ts index 7e8b2cfb2..e9bb70804 100644 --- a/src/renderer/utils/notifications/handlers/issue.test.ts +++ b/src/renderer/utils/notifications/handlers/issue.test.ts @@ -7,15 +7,27 @@ import { } from '../../../__mocks__/notifications-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import { createPartialMockUser } from '../../../__mocks__/user-mocks'; -import { IconColor, type Link } from '../../../types'; +import { + type GitifyIssueState, + type GitifySubject, + IconColor, + type Link, +} from '../../../types'; import type { Notification } from '../../../typesGitHub'; +import type { + FetchIssueByNumberQuery, + IssueState, + IssueStateReason, +} from '../../api/graphql/generated/graphql'; import { issueHandler } from './issue'; +type IssueResponse = FetchIssueByNumberQuery['repository']['issue']; + +const mockAuthor = createPartialMockUser('issue-author'); +const mockCommenter = createPartialMockUser('issue-commenter'); + describe('renderer/utils/notifications/handlers/issue.ts', () => { describe('enrich', () => { - const mockAuthor = createPartialMockUser('some-author'); - const mockCommenter = createPartialMockUser('some-commenter'); - let mockNotification: Notification; beforeEach(() => { @@ -32,351 +44,263 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { axios.defaults.adapter = 'http'; }); - it('open issue state', async () => { - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/1') - .reply(200, { - number: 123, - state: 'open', - user: mockAuthor, - labels: [], - }); - - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - - const result = await issueHandler.enrich(mockNotification, mockSettings); - - expect(result).toEqual({ - number: 123, - state: 'open', - user: { - login: mockCommenter.login, - html_url: mockCommenter.html_url, - avatar_url: mockCommenter.avatar_url, - type: mockCommenter.type, - }, - labels: [], + it('issue with only state', async () => { + const mockIssue = mockIssueResponseNode({ + state: 'OPEN', }); - }); - it('closed issue state', async () => { nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/1') + .post('/graphql') .reply(200, { - number: 123, - state: 'closed', - user: mockAuthor, - labels: [], + data: { + repository: { + issue: mockIssue, + }, + }, }); - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - const result = await issueHandler.enrich(mockNotification, mockSettings); expect(result).toEqual({ number: 123, - state: 'closed', + state: 'OPEN', user: { - login: mockCommenter.login, - html_url: mockCommenter.html_url, - avatar_url: mockCommenter.avatar_url, - type: mockCommenter.type, + login: mockAuthor.login, + html_url: mockAuthor.html_url, + avatar_url: mockAuthor.avatar_url, + type: mockAuthor.type, }, + comments: 0, + htmlUrl: 'https://github.com/gitify-app/notifications-test/issues/123', labels: [], - }); + milestone: null, + } as GitifySubject); }); - it('completed issue state', async () => { + it('issue with stateReason - prefer stateReason over state when available', async () => { + const mockIssue = mockIssueResponseNode({ + state: 'CLOSED', + stateReason: 'COMPLETED', + }); + nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/1') + .post('/graphql') .reply(200, { - number: 123, - state: 'closed', - state_reason: 'completed', - user: mockAuthor, - labels: [], + data: { + repository: { + issue: mockIssue, + }, + }, }); - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - const result = await issueHandler.enrich(mockNotification, mockSettings); expect(result).toEqual({ number: 123, - state: 'completed', + state: 'COMPLETED', user: { - login: mockCommenter.login, - html_url: mockCommenter.html_url, - avatar_url: mockCommenter.avatar_url, - type: mockCommenter.type, + login: mockAuthor.login, + html_url: mockAuthor.html_url, + avatar_url: mockAuthor.avatar_url, + type: mockAuthor.type, }, + comments: 0, + htmlUrl: 'https://github.com/gitify-app/notifications-test/issues/123', labels: [], - }); + milestone: null, + } as GitifySubject); }); - it('not_planned issue state', async () => { + it('issue with comments', async () => { + const mockIssue = mockIssueResponseNode({ + state: 'OPEN', + }); + mockIssue.comments = { + totalCount: 1, + nodes: [ + { + author: mockCommenter, + url: 'https://github.com/gitify-app/notifications-test/issues/123#issuecomment-1234', + }, + ], + }; + nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/1') + .post('/graphql') .reply(200, { - number: 123, - state: 'open', - state_reason: 'not_planned', - user: mockAuthor, - labels: [], + data: { + repository: { + issue: mockIssue, + }, + }, }); - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - const result = await issueHandler.enrich(mockNotification, mockSettings); expect(result).toEqual({ number: 123, - state: 'not_planned', + state: 'OPEN', user: { login: mockCommenter.login, html_url: mockCommenter.html_url, avatar_url: mockCommenter.avatar_url, type: mockCommenter.type, }, + comments: 1, + htmlUrl: + 'https://github.com/gitify-app/notifications-test/issues/123#issuecomment-1234', labels: [], - }); + milestone: null, + } as GitifySubject); }); - it('reopened issue state', async () => { + it('with labels', async () => { + const mockIssue = mockIssueResponseNode({ + state: 'OPEN', + }); + mockIssue.labels = { + nodes: [{ name: 'enhancement' }], + }; + nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/1') + .post('/graphql') .reply(200, { - number: 123, - state: 'open', - state_reason: 'reopened', - user: mockAuthor, - labels: [], + data: { + repository: { + issue: mockIssue, + }, + }, }); - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - const result = await issueHandler.enrich(mockNotification, mockSettings); expect(result).toEqual({ number: 123, - state: 'reopened', + state: 'OPEN', user: { - login: mockCommenter.login, - html_url: mockCommenter.html_url, - avatar_url: mockCommenter.avatar_url, - type: mockCommenter.type, + login: mockAuthor.login, + html_url: mockAuthor.html_url, + avatar_url: mockAuthor.avatar_url, + type: mockAuthor.type, }, - labels: [], - }); + comments: 0, + htmlUrl: 'https://github.com/gitify-app/notifications-test/issues/123', + labels: ['enhancement'], + milestone: null, + } as GitifySubject); }); - it('handle issues without latest_comment_url', async () => { - mockNotification.subject.latest_comment_url = null; + it('with milestone', async () => { + const mockIssue = mockIssueResponseNode({ + state: 'OPEN', + }); + mockIssue.milestone = { + state: 'OPEN', + title: 'Open Milestone', + }; nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/1') + .post('/graphql') .reply(200, { - number: 123, - state: 'open', - draft: false, - merged: false, - user: mockAuthor, - labels: [], + data: { + repository: { + issue: mockIssue, + }, + }, }); const result = await issueHandler.enrich(mockNotification, mockSettings); expect(result).toEqual({ number: 123, - state: 'open', + state: 'OPEN', user: { login: mockAuthor.login, html_url: mockAuthor.html_url, avatar_url: mockAuthor.avatar_url, type: mockAuthor.type, }, + comments: 0, + htmlUrl: 'https://github.com/gitify-app/notifications-test/issues/123', labels: [], - }); - }); - - describe('Issue With Labels', () => { - it('with labels', async () => { - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/1') - .reply(200, { - number: 123, - state: 'open', - user: mockAuthor, - labels: [{ name: 'enhancement' }], - }); - - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - - const result = await issueHandler.enrich( - mockNotification, - mockSettings, - ); - - expect(result).toEqual({ - number: 123, - state: 'open', - user: { - login: mockCommenter.login, - html_url: mockCommenter.html_url, - avatar_url: mockCommenter.avatar_url, - type: mockCommenter.type, - }, - labels: ['enhancement'], - }); - }); - - it('handle null labels', async () => { - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/1') - .reply(200, { - number: 123, - state: 'open', - user: mockAuthor, - labels: null, - }); - - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - - const result = await issueHandler.enrich( - mockNotification, - mockSettings, - ); - - expect(result).toEqual({ - number: 123, - state: 'open', - user: { - login: mockCommenter.login, - html_url: mockCommenter.html_url, - avatar_url: mockCommenter.avatar_url, - type: mockCommenter.type, - }, - labels: [], - }); - }); + milestone: { + state: 'OPEN', + title: 'Open Milestone', + }, + } as GitifySubject); }); + }); - it('early return if issue state filtered out', async () => { - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/1') - .reply(200, { - number: 123, - state: 'open', - user: mockAuthor, - labels: [], - }); - - const result = await issueHandler.enrich(mockNotification, { - ...mockSettings, - filterStates: ['closed'], - }); - - expect(result).toEqual(null); + describe('iconType', () => { + const cases = { + CLOSED: 'IssueClosedIcon', + COMPLETED: 'IssueClosedIcon', + DUPLICATE: 'SkipIcon', + NOT_PLANNED: 'SkipIcon', + OPEN: 'IssueOpenedIcon', + REOPENED: 'IssueReopenedIcon', + } satisfies Record; + + it.each( + Object.entries(cases) as Array<[GitifyIssueState, IconColor]>, + )('iconType for issue with state %s', (issueState, issueIconType) => { + expect( + issueHandler.iconType( + createMockSubject({ type: 'Issue', state: issueState }), + ).displayName, + ).toBe(issueIconType); }); }); - it('iconType', () => { - expect( - issueHandler.iconType(createMockSubject({ type: 'Issue' })).displayName, - ).toBe('IssueOpenedIcon'); - - expect( - issueHandler.iconType( - createMockSubject({ type: 'Issue', state: 'draft' }), - ).displayName, - ).toBe('IssueDraftIcon'); - - expect( - issueHandler.iconType( - createMockSubject({ - type: 'Issue', - state: 'closed', - }), - ).displayName, - ).toBe('IssueClosedIcon'); - - expect( - issueHandler.iconType( - createMockSubject({ - type: 'Issue', - state: 'completed', - }), - ).displayName, - ).toBe('IssueClosedIcon'); - - expect( - issueHandler.iconType( - createMockSubject({ - type: 'Issue', - state: 'not_planned', - }), - ).displayName, - ).toBe('SkipIcon'); - - expect( - issueHandler.iconType( - createMockSubject({ - type: 'Issue', - state: 'reopened', - }), - ).displayName, - ).toBe('IssueReopenedIcon'); + describe('iconColor', () => { + const cases = { + CLOSED: IconColor.RED, + COMPLETED: IconColor.PURPLE, + DUPLICATE: IconColor.GRAY, + NOT_PLANNED: IconColor.GRAY, + OPEN: IconColor.GREEN, + REOPENED: IconColor.GREEN, + } satisfies Record; + + it.each( + Object.entries(cases) as Array<[GitifyIssueState, IconColor]>, + )('iconColor for issue with state %s', (issueState, issueIconColor) => { + expect( + issueHandler.iconColor( + createMockSubject({ type: 'Issue', state: issueState }), + ), + ).toBe(issueIconColor); + }); }); - it('iconColor', () => { - expect( - issueHandler.iconColor( - createMockSubject({ type: 'Issue', state: 'open' }), - ), - ).toBe(IconColor.GREEN); - - expect( - issueHandler.iconColor( - createMockSubject({ type: 'Issue', state: 'reopened' }), - ), - ).toBe(IconColor.GREEN); - - expect( - issueHandler.iconColor( - createMockSubject({ type: 'Issue', state: 'closed' }), - ), - ).toBe(IconColor.RED); - - expect( - issueHandler.iconColor( - createMockSubject({ type: 'Issue', state: 'completed' }), - ), - ).toBe(IconColor.PURPLE); + it('defaultUrl', () => { + const mockHtmlUrl = + 'https://github.com/gitify-app/notifications-test' as Link; expect( - issueHandler.iconColor( - createMockSubject({ type: 'Issue', state: 'draft' }), - ), - ).toBe(IconColor.GRAY); - - expect( - issueHandler.iconColor( - createMockSubject({ type: 'Issue', state: 'not_planned' }), - ), - ).toBe(IconColor.GRAY); + issueHandler.defaultUrl({ + repository: { + html_url: mockHtmlUrl, + }, + } as Notification), + ).toEqual(`${mockHtmlUrl}/issues`); }); }); + +function mockIssueResponseNode(mocks: { + state: IssueState; + stateReason?: IssueStateReason; +}): IssueResponse { + return { + __typename: 'Issue', + number: 123, + title: 'PR Title', + state: mocks.state, + stateReason: mocks.stateReason, + url: 'https://github.com/gitify-app/notifications-test/issues/123', + author: mockAuthor, + labels: { nodes: [] }, + comments: { totalCount: 0, nodes: [] }, + milestone: null, + }; +} diff --git a/src/renderer/utils/notifications/handlers/issue.ts b/src/renderer/utils/notifications/handlers/issue.ts index 868bbe3bb..2a1fb9206 100644 --- a/src/renderer/utils/notifications/handlers/issue.ts +++ b/src/renderer/utils/notifications/handlers/issue.ts @@ -3,75 +3,62 @@ import type { FC } from 'react'; import type { OcticonProps } from '@primer/octicons-react'; import { IssueClosedIcon, - IssueDraftIcon, IssueOpenedIcon, IssueReopenedIcon, SkipIcon, } from '@primer/octicons-react'; -import type { SettingsState } from '../../../types'; -import { IconColor } from '../../../types'; import type { + GitifyIssueState, GitifySubject, - Notification, - Subject, - User, -} from '../../../typesGitHub'; -import { getIssue, getIssueOrPullRequestComment } from '../../api/client'; -import { isStateFilteredOut } from '../filters/filter'; + Link, + SettingsState, +} from '../../../types'; +import { IconColor } from '../../../types'; +import type { Notification, Subject } from '../../../typesGitHub'; +import { fetchIssueByNumber } from '../../api/client'; import { DefaultHandler, defaultHandler } from './default'; -import { getSubjectUser } from './utils'; +import { getNotificationAuthor } from './utils'; class IssueHandler extends DefaultHandler { readonly type = 'Issue'; async enrich( notification: Notification, - settings: SettingsState, + _settings: SettingsState, ): Promise { - const issue = ( - await getIssue(notification.subject.url, notification.account.token) - ).data; + const response = await fetchIssueByNumber(notification); + const issue = response.data.repository?.issue; - const issueState = issue.state_reason ?? issue.state; + const issueState = issue.stateReason ?? issue.state; - // Return early if this notification would be hidden by filters - if (isStateFilteredOut(issueState, settings)) { - return null; - } + const issueComment = issue.comments.nodes[0]; - let issueCommentUser: User; - - if (notification.subject.latest_comment_url) { - const issueComment = ( - await getIssueOrPullRequestComment( - notification.subject.latest_comment_url, - notification.account.token, - ) - ).data; - issueCommentUser = issueComment.user; - } + const issueUser = getNotificationAuthor([ + issueComment?.author, + issue.author, + ]); return { number: issue.number, state: issueState, - user: getSubjectUser([issueCommentUser, issue.user]), - comments: issue.comments, - labels: issue.labels?.map((label) => label.name) ?? [], + user: issueUser, + comments: issue.comments.totalCount, + labels: issue.labels?.nodes.map((label) => label.name), milestone: issue.milestone, + htmlUrl: issueComment?.url ?? issue.url, }; } iconType(subject: Subject): FC | null { - switch (subject.state) { - case 'draft': - return IssueDraftIcon; - case 'closed': - case 'completed': + switch (subject.state as GitifyIssueState) { + case 'CLOSED': + case 'COMPLETED': return IssueClosedIcon; - case 'not_planned': + case 'DUPLICATE': + case 'NOT_PLANNED': return SkipIcon; - case 'reopened': + case 'REOPENED': return IssueReopenedIcon; default: return IssueOpenedIcon; @@ -79,18 +66,24 @@ class IssueHandler extends DefaultHandler { } iconColor(subject: Subject): IconColor { - switch (subject.state) { - case 'open': - case 'reopened': + switch (subject.state as GitifyIssueState) { + case 'OPEN': + case 'REOPENED': return IconColor.GREEN; - case 'closed': + case 'CLOSED': return IconColor.RED; - case 'completed': + case 'COMPLETED': return IconColor.PURPLE; default: return defaultHandler.iconColor(subject); } } + + defaultUrl(notification: Notification): Link { + const url = new URL(notification.repository.html_url); + url.pathname += '/issues'; + return url.href as Link; + } } export const issueHandler = new IssueHandler(); diff --git a/src/renderer/utils/notifications/handlers/pullRequest.test.ts b/src/renderer/utils/notifications/handlers/pullRequest.test.ts index 494267779..d081f0cc3 100644 --- a/src/renderer/utils/notifications/handlers/pullRequest.test.ts +++ b/src/renderer/utils/notifications/handlers/pullRequest.test.ts @@ -7,13 +7,25 @@ import { } from '../../../__mocks__/notifications-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import { createPartialMockUser } from '../../../__mocks__/user-mocks'; -import { IconColor, type Link } from '../../../types'; -import type { Notification, PullRequest } from '../../../typesGitHub'; import { - getLatestReviewForReviewers, - parseLinkedIssuesFromPr, - pullRequestHandler, -} from './pullRequest'; + type GitifyPullRequestState, + type GitifySubject, + IconColor, + type Link, +} from '../../../types'; +import type { Notification } from '../../../typesGitHub'; +import type { + FetchPullRequestByNumberQuery, + PullRequestReviewState, + PullRequestState, +} from '../../api/graphql/generated/graphql'; +import { getLatestReviewForReviewers, pullRequestHandler } from './pullRequest'; + +type PullRequestResponse = + FetchPullRequestByNumberQuery['repository']['pullRequest']; + +const mockAuthor = createPartialMockUser('some-author'); +const mockCommenter = createPartialMockUser('some-commenter'); describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { let mockNotification: Notification; @@ -29,35 +41,25 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { }); describe('enrich', () => { - const mockAuthor = createPartialMockUser('some-author'); - const mockCommenter = createPartialMockUser('some-commenter'); - beforeEach(() => { // axios will default to using the XHR adapter which can't be intercepted // by nock. So, configure axios to use the node adapter. axios.defaults.adapter = 'http'; }); - it('closed pull request state', async () => { + it('pull request with state', async () => { + const mockPullRequest = mockPullRequestResponseNode({ state: 'CLOSED' }); + nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1') + .post('/graphql') .reply(200, { - number: 123, - state: 'closed', - draft: false, - merged: false, - user: mockAuthor, - labels: [], + data: { + repository: { + pullRequest: mockPullRequest, + }, + }, }); - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1/reviews') - .reply(200, []); - const result = await pullRequestHandler.enrich( mockNotification, mockSettings, @@ -65,39 +67,38 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { expect(result).toEqual({ number: 123, - state: 'closed', + state: 'CLOSED', user: { - login: mockCommenter.login, - html_url: mockCommenter.html_url, - avatar_url: mockCommenter.avatar_url, - type: mockCommenter.type, + login: mockAuthor.login, + html_url: mockAuthor.html_url, + avatar_url: mockAuthor.avatar_url, + type: mockAuthor.type, }, reviews: null, labels: [], linkedIssues: [], - }); + comments: 0, + milestone: null, + htmlUrl: 'https://github.com/gitify-app/notifications-test/pulls/123', + } as GitifySubject); }); it('draft pull request state', async () => { + const mockPullRequest = mockPullRequestResponseNode({ + state: 'OPEN', + isDraft: true, + }); + nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1') + .post('/graphql') .reply(200, { - number: 123, - state: 'open', - draft: true, - merged: false, - user: mockAuthor, - labels: [], + data: { + repository: { + pullRequest: mockPullRequest, + }, + }, }); - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1/reviews') - .reply(200, []); - const result = await pullRequestHandler.enrich( mockNotification, mockSettings, @@ -105,39 +106,38 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { expect(result).toEqual({ number: 123, - state: 'draft', + state: 'DRAFT', user: { - login: mockCommenter.login, - html_url: mockCommenter.html_url, - avatar_url: mockCommenter.avatar_url, - type: mockCommenter.type, + login: mockAuthor.login, + html_url: mockAuthor.html_url, + avatar_url: mockAuthor.avatar_url, + type: mockAuthor.type, }, reviews: null, labels: [], linkedIssues: [], - }); + comments: 0, + milestone: null, + htmlUrl: 'https://github.com/gitify-app/notifications-test/pulls/123', + } as GitifySubject); }); it('merged pull request state', async () => { + const mockPullRequest = mockPullRequestResponseNode({ + state: 'MERGED', + merged: true, + }); + nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1') + .post('/graphql') .reply(200, { - number: 123, - state: 'open', - draft: false, - merged: true, - user: mockAuthor, - labels: [], + data: { + repository: { + pullRequest: mockPullRequest, + }, + }, }); - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1/reviews') - .reply(200, []); - const result = await pullRequestHandler.enrich( mockNotification, mockSettings, @@ -145,39 +145,46 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { expect(result).toEqual({ number: 123, - state: 'merged', + state: 'MERGED', user: { - login: mockCommenter.login, - html_url: mockCommenter.html_url, - avatar_url: mockCommenter.avatar_url, - type: mockCommenter.type, + login: mockAuthor.login, + html_url: mockAuthor.html_url, + avatar_url: mockAuthor.avatar_url, + type: mockAuthor.type, }, reviews: null, labels: [], linkedIssues: [], - }); + comments: 0, + milestone: null, + htmlUrl: 'https://github.com/gitify-app/notifications-test/pulls/123', + } as GitifySubject); }); - it('open pull request state', async () => { + it('with comments', async () => { + const mockPullRequest = mockPullRequestResponseNode({ + state: 'OPEN', + }); + mockPullRequest.comments = { + totalCount: 1, + nodes: [ + { + author: mockCommenter, + url: 'https://github.com/gitify-app/notifications-test/pulls/123#issuecomment-1234', + }, + ], + }; + nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1') + .post('/graphql') .reply(200, { - number: 123, - state: 'open', - draft: false, - merged: false, - user: mockAuthor, - labels: [], + data: { + repository: { + pullRequest: mockPullRequest, + }, + }, }); - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1/reviews') - .reply(200, []); - const result = await pullRequestHandler.enrich( mockNotification, mockSettings, @@ -185,7 +192,7 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { expect(result).toEqual({ number: 123, - state: 'open', + state: 'OPEN', user: { login: mockCommenter.login, html_url: mockCommenter.html_url, @@ -195,28 +202,35 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { reviews: null, labels: [], linkedIssues: [], - }); + comments: 1, + milestone: null, + htmlUrl: + 'https://github.com/gitify-app/notifications-test/pulls/123#issuecomment-1234', + } as GitifySubject); }); - it('avoid fetching comments if latest_comment_url and url are the same', async () => { - mockNotification.subject.latest_comment_url = - mockNotification.subject.url; + it('with labels', async () => { + const mockPullRequest = mockPullRequestResponseNode({ + state: 'OPEN', + }); + mockPullRequest.labels = { + nodes: [ + { + name: 'enhancement', + }, + ], + }; nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1') + .post('/graphql') .reply(200, { - number: 123, - state: 'open', - draft: false, - merged: false, - user: mockAuthor, - labels: [], + data: { + repository: { + pullRequest: mockPullRequest, + }, + }, }); - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1/reviews') - .reply(200, []); - const result = await pullRequestHandler.enrich( mockNotification, mockSettings, @@ -224,7 +238,7 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { expect(result).toEqual({ number: 123, - state: 'open', + state: 'OPEN', user: { login: mockAuthor.login, html_url: mockAuthor.html_url, @@ -232,29 +246,36 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { type: mockAuthor.type, }, reviews: null, - labels: [], + labels: ['enhancement'], linkedIssues: [], - }); + comments: 0, + milestone: null, + htmlUrl: 'https://github.com/gitify-app/notifications-test/pulls/123', + } as GitifySubject); }); - it('handle pull request without latest_comment_url', async () => { - mockNotification.subject.latest_comment_url = null; + it('with linked issues', async () => { + const mockPullRequest = mockPullRequestResponseNode({ + state: 'OPEN', + }); + mockPullRequest.closingIssuesReferences = { + nodes: [ + { + number: 789, + }, + ], + }; nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1') + .post('/graphql') .reply(200, { - number: 123, - state: 'open', - draft: false, - merged: false, - user: mockAuthor, - labels: [], + data: { + repository: { + pullRequest: mockPullRequest, + }, + }, }); - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1/reviews') - .reply(200, []); - const result = await pullRequestHandler.enrich( mockNotification, mockSettings, @@ -262,7 +283,7 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { expect(result).toEqual({ number: 123, - state: 'open', + state: 'OPEN', user: { login: mockAuthor.login, html_url: mockAuthor.html_url, @@ -271,271 +292,140 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { }, reviews: null, labels: [], - linkedIssues: [], - }); + linkedIssues: ['#789'], + comments: 0, + milestone: null, + htmlUrl: 'https://github.com/gitify-app/notifications-test/pulls/123', + } as GitifySubject); }); - describe('Pull Requests With Labels', () => { - it('with labels', async () => { - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1') - .reply(200, { - number: 123, - state: 'open', - draft: false, - merged: false, - user: mockAuthor, - labels: [{ name: 'enhancement' }], - }); - - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1/reviews') - .reply(200, []); - - const result = await pullRequestHandler.enrich( - mockNotification, - mockSettings, - ); - - expect(result).toEqual({ - number: 123, - state: 'open', - user: { - login: mockCommenter.login, - html_url: mockCommenter.html_url, - avatar_url: mockCommenter.avatar_url, - type: mockCommenter.type, - }, - reviews: null, - labels: ['enhancement'], - linkedIssues: [], - }); + it('with milestone', async () => { + const mockPullRequest = mockPullRequestResponseNode({ + state: 'OPEN', }); + mockPullRequest.milestone = { + state: 'OPEN', + title: 'Open Milestone', + }; - it('handle null labels', async () => { - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1') - .reply(200, { - number: 123, - state: 'open', - draft: false, - merged: false, - user: mockAuthor, - labels: null, - }); - - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1/reviews') - .reply(200, []); - - const result = await pullRequestHandler.enrich( - mockNotification, - mockSettings, - ); - - expect(result).toEqual({ - number: 123, - state: 'open', - user: { - login: mockCommenter.login, - html_url: mockCommenter.html_url, - avatar_url: mockCommenter.avatar_url, - type: mockCommenter.type, - }, - reviews: null, - labels: [], - linkedIssues: [], - }); - }); - }); - - describe('Pull Request With Linked Issues', () => { - it('returns empty if no pr body', () => { - const mockPr = { - user: { - type: 'User', - }, - body: null, - } as PullRequest; - - const result = parseLinkedIssuesFromPr(mockPr); - expect(result).toEqual([]); - }); - - it('returns empty if pr from non-user', () => { - const mockPr = { - user: { - type: 'Bot', - }, - body: 'This PR is linked to #1, #2, and #3', - } as PullRequest; - const result = parseLinkedIssuesFromPr(mockPr); - expect(result).toEqual([]); - }); - - it('returns linked issues', () => { - const mockPr = { - user: { - type: 'User', - }, - body: 'This PR is linked to #1, #2, and #3', - } as PullRequest; - const result = parseLinkedIssuesFromPr(mockPr); - expect(result).toEqual(['#1', '#2', '#3']); - }); - }); - - it('early return if pull request state filtered', async () => { nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1') + .post('/graphql') .reply(200, { - number: 123, - state: 'open', - draft: false, - merged: false, - user: mockAuthor, - labels: [], + data: { + repository: { + pullRequest: mockPullRequest, + }, + }, }); - const result = await pullRequestHandler.enrich(mockNotification, { - ...mockSettings, - filterStates: ['closed'], - }); + const result = await pullRequestHandler.enrich( + mockNotification, + mockSettings, + ); - expect(result).toEqual(null); + expect(result).toEqual({ + number: 123, + state: 'OPEN', + user: { + login: mockAuthor.login, + html_url: mockAuthor.html_url, + avatar_url: mockAuthor.avatar_url, + type: mockAuthor.type, + }, + reviews: null, + labels: [], + linkedIssues: [], + comments: 0, + milestone: { + state: 'OPEN', + title: 'Open Milestone', + }, + htmlUrl: 'https://github.com/gitify-app/notifications-test/pulls/123', + } as GitifySubject); }); + }); - it('early return if pull request user filtered', async () => { - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1') - .reply(200, { - number: 123, - state: 'open', - draft: false, - merged: false, - user: mockAuthor, - labels: [], - }); - - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/comments/302888448') - .reply(200, { user: mockCommenter }); - - const result = await pullRequestHandler.enrich(mockNotification, { - ...mockSettings, - filterUserTypes: ['Bot'], - }); - - expect(result).toEqual(null); + describe('iconType', () => { + const cases = { + CLOSED: 'GitPullRequestClosedIcon', + DRAFT: 'GitPullRequestDraftIcon', + MERGED: 'GitMergeIcon', + OPEN: 'GitPullRequestIcon', + } satisfies Record; + + it.each( + Object.entries(cases) as Array<[GitifyPullRequestState, IconColor]>, + )('iconType for pull request with state %s', (pullRequestState, pullRequestIconType) => { + expect( + pullRequestHandler.iconType( + createMockSubject({ type: 'PullRequest', state: pullRequestState }), + ).displayName, + ).toBe(pullRequestIconType); }); }); - it('iconType', () => { - expect( - pullRequestHandler.iconType(createMockSubject({ type: 'PullRequest' })) - .displayName, - ).toBe('GitPullRequestIcon'); - - expect( - pullRequestHandler.iconType( - createMockSubject({ - type: 'PullRequest', - state: 'draft', - }), - ).displayName, - ).toBe('GitPullRequestDraftIcon'); - - expect( - pullRequestHandler.iconType( - createMockSubject({ - type: 'PullRequest', - state: 'closed', - }), - ).displayName, - ).toBe('GitPullRequestClosedIcon'); - - expect( - pullRequestHandler.iconType( - createMockSubject({ - type: 'PullRequest', - state: 'merged', - }), - ).displayName, - ).toBe('GitMergeIcon'); + describe('iconColor', () => { + const cases = { + CLOSED: IconColor.RED, + DRAFT: IconColor.GRAY, + MERGED: IconColor.PURPLE, + OPEN: IconColor.GREEN, + } satisfies Record; + + it.each( + Object.entries(cases) as Array<[GitifyPullRequestState, IconColor]>, + )('iconType for pull request with state %s', (pullRequestState, pullRequestIconColor) => { + expect( + pullRequestHandler.iconColor( + createMockSubject({ type: 'PullRequest', state: pullRequestState }), + ), + ).toBe(pullRequestIconColor); + }); }); - it('iconColor', () => { - expect( - pullRequestHandler.iconColor( - createMockSubject({ type: 'PullRequest', state: 'open' }), - ), - ).toBe(IconColor.GREEN); - - expect( - pullRequestHandler.iconColor( - createMockSubject({ type: 'PullRequest', state: 'reopened' }), - ), - ).toBe(IconColor.GREEN); - - expect( - pullRequestHandler.iconColor( - createMockSubject({ type: 'PullRequest', state: 'closed' }), - ), - ).toBe(IconColor.RED); - - expect( - pullRequestHandler.iconColor( - createMockSubject({ type: 'PullRequest', state: 'merged' }), - ), - ).toBe(IconColor.PURPLE); + it('defaultUrl', () => { + const mockHtmlUrl = + 'https://github.com/gitify-app/notifications-test' as Link; expect( - pullRequestHandler.iconColor( - createMockSubject({ type: 'PullRequest', state: 'draft' }), - ), - ).toBe(IconColor.GRAY); + pullRequestHandler.defaultUrl({ + repository: { + html_url: mockHtmlUrl, + }, + } as Notification), + ).toEqual(`${mockHtmlUrl}/pulls`); }); describe('Pull Request Reviews - Latest Reviews By Reviewer', () => { it('returns latest review state per reviewer', async () => { - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1/reviews') - .reply(200, [ - { - user: { - login: 'reviewer-1', - }, - state: 'REQUESTED_CHANGES', + const mockReviews = [ + { + author: { + login: 'reviewer-1', }, - { - user: { - login: 'reviewer-2', - }, - state: 'COMMENTED', + state: 'CHANGES_REQUESTED' as PullRequestReviewState, + }, + { + author: { + login: 'reviewer-2', }, - { - user: { - login: 'reviewer-1', - }, - state: 'APPROVED', + state: 'COMMENTED' as PullRequestReviewState, + }, + { + author: { + login: 'reviewer-1', }, - { - user: { - login: 'reviewer-3', - }, - state: 'APPROVED', + state: 'APPROVED' as PullRequestReviewState, + }, + { + author: { + login: 'reviewer-3', }, - ]); + state: 'APPROVED' as PullRequestReviewState, + }, + ]; - const result = await getLatestReviewForReviewers(mockNotification); + const result = getLatestReviewForReviewers(mockReviews); expect(result).toEqual([ { state: 'APPROVED', users: ['reviewer-3', 'reviewer-1'] }, @@ -544,21 +434,40 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { }); it('handles no PR reviews yet', async () => { - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/pulls/1/reviews') - .reply(200, []); - - const result = await getLatestReviewForReviewers(mockNotification); - - expect(result).toBeNull(); - }); - - it('returns null when not a PR notification', async () => { - mockNotification.subject.type = 'Issue'; - - const result = await getLatestReviewForReviewers(mockNotification); + const result = getLatestReviewForReviewers([]); expect(result).toBeNull(); }); }); }); + +function mockPullRequestResponseNode(mocks: { + state: PullRequestState; + isDraft?: boolean; + merged?: boolean; +}): PullRequestResponse { + return { + __typename: 'PullRequest', + number: 123, + title: 'Test PR', + state: mocks.state, + isDraft: mocks.isDraft ?? false, + merged: mocks.merged ?? false, + isInMergeQueue: false, + url: 'https://github.com/gitify-app/notifications-test/pulls/123', + author: mockAuthor, + labels: { nodes: [] }, + comments: { + totalCount: 0, + nodes: [], + }, + reviews: { + totalCount: 0, + nodes: [], + }, + milestone: null, + closingIssuesReferences: { + nodes: [], + }, + }; +} diff --git a/src/renderer/utils/notifications/handlers/pullRequest.ts b/src/renderer/utils/notifications/handlers/pullRequest.ts index 739c475c8..d3b35b412 100644 --- a/src/renderer/utils/notifications/handlers/pullRequest.ts +++ b/src/renderer/utils/notifications/handlers/pullRequest.ts @@ -8,93 +8,63 @@ import { GitPullRequestIcon, } from '@primer/octicons-react'; -import type { Link, SettingsState } from '../../../types'; -import { IconColor } from '../../../types'; -import type { - GitifyPullRequestReview, - GitifySubject, - Notification, - PullRequest, - PullRequestReview, - PullRequestStateType, - Subject, - User, -} from '../../../typesGitHub'; import { - getIssueOrPullRequestComment, - getPullRequest, - getPullRequestReviews, -} from '../../api/client'; -import { isStateFilteredOut, isUserFilteredOut } from '../filters/filter'; + type GitifyPullRequestReview, + type GitifyPullRequestState, + type GitifySubject, + IconColor, + type Link, + type SettingsState, +} from '../../../types'; +import type { Notification, Subject } from '../../../typesGitHub'; +import { fetchPullByNumber } from '../../api/client'; +import type { FetchPullRequestByNumberQuery } from '../../api/graphql/generated/graphql'; import { DefaultHandler, defaultHandler } from './default'; -import { getSubjectUser } from './utils'; +import { getNotificationAuthor } from './utils'; class PullRequestHandler extends DefaultHandler { readonly type = 'PullRequest' as const; async enrich( notification: Notification, - settings: SettingsState, + _settings: SettingsState, ): Promise { - const pr = ( - await getPullRequest(notification.subject.url, notification.account.token) - ).data; - - let prState: PullRequestStateType = pr.state; - if (pr.merged) { - prState = 'merged'; - } else if (pr.draft) { - prState = 'draft'; - } + const response = await fetchPullByNumber(notification); + const pr = response.data.repository.pullRequest; - // Return early if this notification would be hidden by state filters - if (isStateFilteredOut(prState, settings)) { - return null; + let prState: GitifyPullRequestState = pr.state; + if (pr.isDraft) { + prState = 'DRAFT'; } - let prCommentUser: User; - if ( - notification.subject.latest_comment_url && - notification.subject.latest_comment_url !== notification.subject.url - ) { - const prComment = ( - await getIssueOrPullRequestComment( - notification.subject.latest_comment_url, - notification.account.token, - ) - ).data; - prCommentUser = prComment.user; - } + const prComment = pr.comments?.nodes[0]; - const prUser = getSubjectUser([prCommentUser, pr.user]); + const prUser = getNotificationAuthor([prComment?.author, pr.author]); - // Return early if this notification would be hidden by user filters - if (isUserFilteredOut(prUser, settings)) { - return null; - } - - const reviews = await getLatestReviewForReviewers(notification); - const linkedIssues = parseLinkedIssuesFromPr(pr); + const reviews = getLatestReviewForReviewers(pr.reviews.nodes); return { number: pr.number, state: prState, user: prUser, reviews: reviews, - comments: pr.comments, - labels: pr.labels?.map((label) => label.name) ?? [], - linkedIssues: linkedIssues, + comments: pr.comments.totalCount, + labels: pr.labels?.nodes.map((label) => label.name), + linkedIssues: pr.closingIssuesReferences?.nodes.map( + (issue) => `#${issue.number}`, + ), milestone: pr.milestone, + htmlUrl: prComment?.url ?? pr.url, }; } iconType(subject: Subject): FC | null { - switch (subject.state) { - case 'draft': + switch (subject.state as GitifyPullRequestState) { + case 'DRAFT': return GitPullRequestDraftIcon; - case 'closed': + case 'CLOSED': return GitPullRequestClosedIcon; - case 'merged': + case 'MERGED': return GitMergeIcon; default: return GitPullRequestIcon; @@ -102,44 +72,40 @@ class PullRequestHandler extends DefaultHandler { } iconColor(subject: Subject): IconColor { - switch (subject.state) { - case 'open': - case 'reopened': + switch (subject.state as GitifyPullRequestState) { + case 'OPEN': return IconColor.GREEN; - case 'closed': + case 'CLOSED': return IconColor.RED; - case 'merged': + case 'MERGED': return IconColor.PURPLE; default: return defaultHandler.iconColor(subject); } } -} -export const pullRequestHandler = new PullRequestHandler(); - -export async function getLatestReviewForReviewers( - notification: Notification, -): Promise | null { - if (notification.subject.type !== 'PullRequest') { - return null; + defaultUrl(notification: Notification): Link { + const url = new URL(notification.repository.html_url); + url.pathname += '/pulls'; + return url.href as Link; } +} - const prReviews = await getPullRequestReviews( - `${notification.subject.url}/reviews` as Link, - notification.account.token, - ); +export const pullRequestHandler = new PullRequestHandler(); - if (!prReviews.data.length) { +export function getLatestReviewForReviewers( + reviews: FetchPullRequestByNumberQuery['repository']['pullRequest']['reviews']['nodes'], +): GitifyPullRequestReview[] { + if (!reviews.length) { return null; } // Find the most recent review for each reviewer - const latestReviews: PullRequestReview[] = []; - const sortedReviews = prReviews.data.slice().reverse(); + const latestReviews = []; + const sortedReviews = reviews.toReversed(); for (const prReview of sortedReviews) { const reviewerFound = latestReviews.find( - (review) => review.user.login === prReview.user.login, + (review) => review.author.login === prReview.author.login, ); if (!reviewerFound) { @@ -155,11 +121,11 @@ export async function getLatestReviewForReviewers( ); if (reviewerFound) { - reviewerFound.users.push(prReview.user.login); + reviewerFound.users.push(prReview.author.login); } else { reviewers.push({ state: prReview.state, - users: [prReview.user.login], + users: [prReview.author.login], }); } } @@ -169,22 +135,3 @@ export async function getLatestReviewForReviewers( return a.state.localeCompare(b.state); }); } - -export function parseLinkedIssuesFromPr(pr: PullRequest): string[] { - const linkedIssues: string[] = []; - - if (!pr.body || pr.user.type !== 'User') { - return linkedIssues; - } - - const regexPattern = /\s?#(\d+)\s?/gi; - const matches = pr.body.matchAll(regexPattern); - - for (const match of matches) { - if (match[0]) { - linkedIssues.push(match[0].trim()); - } - } - - return linkedIssues; -} diff --git a/src/renderer/utils/notifications/handlers/release.test.ts b/src/renderer/utils/notifications/handlers/release.test.ts index 530c8d0f9..27490a9cb 100644 --- a/src/renderer/utils/notifications/handlers/release.test.ts +++ b/src/renderer/utils/notifications/handlers/release.test.ts @@ -8,6 +8,7 @@ import { import { mockSettings } from '../../../__mocks__/state-mocks'; import { createPartialMockUser } from '../../../__mocks__/user-mocks'; import type { Link } from '../../../types'; +import type { Notification } from '../../../typesGitHub'; import { releaseHandler } from './release'; describe('renderer/utils/notifications/handlers/release.ts', () => { @@ -76,4 +77,17 @@ describe('renderer/utils/notifications/handlers/release.ts', () => { ).displayName, ).toBe('TagIcon'); }); + + it('defaultUrl', () => { + const mockHtmlUrl = + 'https://github.com/gitify-app/notifications-test' as Link; + + expect( + releaseHandler.defaultUrl({ + repository: { + html_url: mockHtmlUrl, + }, + } as Notification), + ).toEqual(`${mockHtmlUrl}/releases`); + }); }); diff --git a/src/renderer/utils/notifications/handlers/release.ts b/src/renderer/utils/notifications/handlers/release.ts index 9caadc1a7..9b2fe3149 100644 --- a/src/renderer/utils/notifications/handlers/release.ts +++ b/src/renderer/utils/notifications/handlers/release.ts @@ -3,17 +3,17 @@ import type { FC } from 'react'; import type { OcticonProps } from '@primer/octicons-react'; import { TagIcon } from '@primer/octicons-react'; -import type { SettingsState } from '../../../types'; import type { + GitifyNotificationState, GitifySubject, - Notification, - StateType, - Subject, -} from '../../../typesGitHub'; + Link, + SettingsState, +} from '../../../types'; +import type { Notification, Subject } from '../../../typesGitHub'; import { getRelease } from '../../api/client'; import { isStateFilteredOut } from '../filters/filter'; import { DefaultHandler } from './default'; -import { getSubjectUser } from './utils'; +import { getNotificationAuthor } from './utils'; class ReleaseHandler extends DefaultHandler { readonly type = 'Release'; @@ -22,7 +22,7 @@ class ReleaseHandler extends DefaultHandler { notification: Notification, settings: SettingsState, ): Promise { - const releaseState: StateType = null; // Release notifications are stateless + const releaseState: GitifyNotificationState = null; // Release notifications are stateless // Return early if this notification would be hidden by filters if (isStateFilteredOut(releaseState, settings)) { @@ -35,13 +35,19 @@ class ReleaseHandler extends DefaultHandler { return { state: releaseState, - user: getSubjectUser([release.author]), + user: getNotificationAuthor([release.author]), }; } iconType(_subject: Subject): FC | null { return TagIcon; } + + defaultUrl(notification: Notification): Link { + const url = new URL(notification.repository.html_url); + url.pathname += '/releases'; + return url.href as Link; + } } export const releaseHandler = new ReleaseHandler(); diff --git a/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.test.ts b/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.test.ts index bd99284ab..c754eba5a 100644 --- a/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.test.ts +++ b/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.test.ts @@ -1,4 +1,6 @@ import { createMockSubject } from '../../../__mocks__/notifications-mocks'; +import type { Link } from '../../../types'; +import type { Notification } from '../../../typesGitHub'; import { repositoryDependabotAlertsThreadHandler } from './repositoryDependabotAlertsThread'; describe('renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts', () => { @@ -11,4 +13,17 @@ describe('renderer/utils/notifications/handlers/repositoryDependabotAlertsThread ).displayName, ).toBe('AlertIcon'); }); + + it('defaultUrl', () => { + const mockHtmlUrl = + 'https://github.com/gitify-app/notifications-test' as Link; + + expect( + repositoryDependabotAlertsThreadHandler.defaultUrl({ + repository: { + html_url: mockHtmlUrl, + }, + } as Notification), + ).toEqual(`${mockHtmlUrl}/security/dependabot`); + }); }); diff --git a/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts b/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts index cd06771be..c71747d48 100644 --- a/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts +++ b/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts @@ -3,7 +3,8 @@ import type { FC } from 'react'; import type { OcticonProps } from '@primer/octicons-react'; import { AlertIcon } from '@primer/octicons-react'; -import type { Subject } from '../../../typesGitHub'; +import type { Link } from '../../../types'; +import type { Notification, Subject } from '../../../typesGitHub'; import { DefaultHandler } from './default'; class RepositoryDependabotAlertsThreadHandler extends DefaultHandler { @@ -12,6 +13,12 @@ class RepositoryDependabotAlertsThreadHandler extends DefaultHandler { iconType(_subject: Subject): FC | null { return AlertIcon; } + + defaultUrl(notification: Notification): Link { + const url = new URL(notification.repository.html_url); + url.pathname += '/security/dependabot'; + return url.href as Link; + } } export const repositoryDependabotAlertsThreadHandler = diff --git a/src/renderer/utils/notifications/handlers/repositoryInvitation.test.ts b/src/renderer/utils/notifications/handlers/repositoryInvitation.test.ts index 1ccefa670..a9b08ed5d 100644 --- a/src/renderer/utils/notifications/handlers/repositoryInvitation.test.ts +++ b/src/renderer/utils/notifications/handlers/repositoryInvitation.test.ts @@ -1,4 +1,6 @@ import { createMockSubject } from '../../../__mocks__/notifications-mocks'; +import type { Link } from '../../../types'; +import type { Notification } from '../../../typesGitHub'; import { repositoryInvitationHandler } from './repositoryInvitation'; describe('renderer/utils/notifications/handlers/repositoryInvitation.ts', () => { @@ -11,4 +13,17 @@ describe('renderer/utils/notifications/handlers/repositoryInvitation.ts', () => ).displayName, ).toBe('MailIcon'); }); + + it('defaultUrl', () => { + const mockHtmlUrl = + 'https://github.com/gitify-app/notifications-test' as Link; + + expect( + repositoryInvitationHandler.defaultUrl({ + repository: { + html_url: mockHtmlUrl, + }, + } as Notification), + ).toEqual(`${mockHtmlUrl}/invitations`); + }); }); diff --git a/src/renderer/utils/notifications/handlers/repositoryInvitation.ts b/src/renderer/utils/notifications/handlers/repositoryInvitation.ts index 38bf30470..a5beaea28 100644 --- a/src/renderer/utils/notifications/handlers/repositoryInvitation.ts +++ b/src/renderer/utils/notifications/handlers/repositoryInvitation.ts @@ -2,7 +2,8 @@ import type { FC } from 'react'; import { MailIcon, type OcticonProps } from '@primer/octicons-react'; -import type { Subject } from '../../../typesGitHub'; +import type { Link } from '../../../types'; +import type { Notification, Subject } from '../../../typesGitHub'; import { DefaultHandler } from './default'; class RepositoryInvitationHandler extends DefaultHandler { @@ -11,6 +12,12 @@ class RepositoryInvitationHandler extends DefaultHandler { iconType(_subject: Subject): FC | null { return MailIcon; } + + defaultUrl(notification: Notification): Link { + const url = new URL(notification.repository.html_url); + url.pathname += '/invitations'; + return url.href as Link; + } } export const repositoryInvitationHandler = new RepositoryInvitationHandler(); diff --git a/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.test.ts b/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.test.ts index eea848173..73ae231ca 100644 --- a/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.test.ts +++ b/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.test.ts @@ -1,4 +1,6 @@ import { createMockSubject } from '../../../__mocks__/notifications-mocks'; +import type { Link } from '../../../types'; +import type { Notification } from '../../../typesGitHub'; import { repositoryVulnerabilityAlertHandler } from './repositoryVulnerabilityAlert'; describe('renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.ts', () => { @@ -11,4 +13,17 @@ describe('renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.ts' ).displayName, ).toBe('AlertIcon'); }); + + it('defaultUrl', () => { + const mockHtmlUrl = + 'https://github.com/gitify-app/notifications-test' as Link; + + expect( + repositoryVulnerabilityAlertHandler.defaultUrl({ + repository: { + html_url: mockHtmlUrl, + }, + } as Notification), + ).toEqual(mockHtmlUrl); + }); }); diff --git a/src/renderer/utils/notifications/handlers/types.ts b/src/renderer/utils/notifications/handlers/types.ts index 686e3ef50..ced167101 100644 --- a/src/renderer/utils/notifications/handlers/types.ts +++ b/src/renderer/utils/notifications/handlers/types.ts @@ -2,13 +2,8 @@ import type { FC } from 'react'; import type { OcticonProps } from '@primer/octicons-react'; -import type { SettingsState } from '../../../types'; -import type { - GitifySubject, - Notification, - Subject, - SubjectType, -} from '../../../typesGitHub'; +import type { GitifySubject, Link, SettingsState } from '../../../types'; +import type { Notification, Subject, SubjectType } from '../../../typesGitHub'; export interface NotificationTypeHandler { readonly type?: SubjectType; @@ -45,4 +40,9 @@ export interface NotificationTypeHandler { * Return the formatted notification title for this notification. */ formattedNotificationTitle(notification: Notification): string; + + /** + * Default url for notification type. + */ + defaultUrl(notification: Notification): Link; } diff --git a/src/renderer/utils/notifications/handlers/utils.test.ts b/src/renderer/utils/notifications/handlers/utils.test.ts index b16f1d812..3b18b6a94 100644 --- a/src/renderer/utils/notifications/handlers/utils.test.ts +++ b/src/renderer/utils/notifications/handlers/utils.test.ts @@ -1,18 +1,18 @@ import { createPartialMockUser } from '../../../__mocks__/user-mocks'; -import { formatForDisplay, getSubjectUser } from './utils'; +import { formatForDisplay, getNotificationAuthor } from './utils'; describe('renderer/utils/notifications/handlers/utils.ts', () => { - describe('getSubjectUser', () => { + describe('getNotificationAuthor', () => { const mockAuthor = createPartialMockUser('some-author'); it('returns null when all users are null', () => { - const result = getSubjectUser([null, null]); + const result = getNotificationAuthor([null, null]); expect(result).toBeNull(); }); it('returns first user', () => { - const result = getSubjectUser([mockAuthor, null]); + const result = getNotificationAuthor([mockAuthor, null]); expect(result).toEqual({ login: mockAuthor.login, @@ -23,7 +23,7 @@ describe('renderer/utils/notifications/handlers/utils.ts', () => { }); it('returns second user if first is null', () => { - const result = getSubjectUser([null, mockAuthor]); + const result = getNotificationAuthor([null, mockAuthor]); expect(result).toEqual({ login: mockAuthor.login, diff --git a/src/renderer/utils/notifications/handlers/utils.ts b/src/renderer/utils/notifications/handlers/utils.ts index e8dc1b13b..4f9187d36 100644 --- a/src/renderer/utils/notifications/handlers/utils.ts +++ b/src/renderer/utils/notifications/handlers/utils.ts @@ -1,12 +1,14 @@ -import type { SubjectUser, User } from '../../../typesGitHub'; +import type { GitifyNotificationUser } from '../../../types'; /** * Construct the notification subject user based on an order prioritized list of users * @param users array of users in order or priority * @returns the subject user */ -export function getSubjectUser(users: User[]): SubjectUser { - let subjectUser: SubjectUser = null; +export function getNotificationAuthor( + users: GitifyNotificationUser[], +): GitifyNotificationUser { + let subjectUser: GitifyNotificationUser = null; for (const user of users) { if (user) { diff --git a/src/renderer/utils/notifications/handlers/workflowRun.test.ts b/src/renderer/utils/notifications/handlers/workflowRun.test.ts index 7d95c4b41..b5c156a9d 100644 --- a/src/renderer/utils/notifications/handlers/workflowRun.test.ts +++ b/src/renderer/utils/notifications/handlers/workflowRun.test.ts @@ -3,6 +3,8 @@ import { createPartialMockNotification, } from '../../../__mocks__/notifications-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; +import type { Link } from '../../../types'; +import type { Notification } from '../../../typesGitHub'; import { getWorkflowRunAttributes, workflowRunHandler } from './workflowRun'; describe('renderer/utils/notifications/handlers/workflowRun.ts', () => { @@ -19,8 +21,10 @@ describe('renderer/utils/notifications/handlers/workflowRun.ts', () => { ); expect(result).toEqual({ - state: 'waiting', + state: 'WAITING', user: null, + htmlUrl: + 'https://github.com/gitify-app/notifications-test/actions?query=is%3AWAITING', }); }); @@ -64,6 +68,22 @@ describe('renderer/utils/notifications/handlers/workflowRun.ts', () => { ).toBe('RocketIcon'); }); + it('defaultUrl', () => { + const mockHtmlUrl = + 'https://github.com/gitify-app/notifications-test' as Link; + + expect( + workflowRunHandler.defaultUrl({ + subject: { + title: 'Some notification', + }, + repository: { + html_url: mockHtmlUrl, + }, + } as Notification), + ).toEqual(`${mockHtmlUrl}/actions`); + }); + describe('getWorkflowRunAttributes', () => { it('deploy review workflow run state', async () => { const mockNotification = createPartialMockNotification({ @@ -74,7 +94,7 @@ describe('renderer/utils/notifications/handlers/workflowRun.ts', () => { const result = getWorkflowRunAttributes(mockNotification); expect(result).toEqual({ - status: 'waiting', + status: 'WAITING', statusDisplayName: 'review', user: 'some-user', }); diff --git a/src/renderer/utils/notifications/handlers/workflowRun.ts b/src/renderer/utils/notifications/handlers/workflowRun.ts index 9dfd7df2e..2eaa8f1b0 100644 --- a/src/renderer/utils/notifications/handlers/workflowRun.ts +++ b/src/renderer/utils/notifications/handlers/workflowRun.ts @@ -3,16 +3,22 @@ import type { FC } from 'react'; import type { OcticonProps } from '@primer/octicons-react'; import { RocketIcon } from '@primer/octicons-react'; -import type { SettingsState } from '../../../types'; import type { - CheckSuiteStatus, + GitifyCheckSuiteStatus, GitifySubject, - Notification, - Subject, - WorkflowRunAttributes, -} from '../../../typesGitHub'; + Link, + SettingsState, +} from '../../../types'; +import type { Notification, Subject } from '../../../typesGitHub'; +import { actionsURL } from '../../helpers'; import { DefaultHandler } from './default'; +export interface WorkflowRunAttributes { + user: string; + statusDisplayName: string; + status: GitifyCheckSuiteStatus | null; +} + class WorkflowRunHandler extends DefaultHandler { readonly type = 'WorkflowRun'; @@ -26,6 +32,7 @@ class WorkflowRunHandler extends DefaultHandler { return { state: state, user: null, + htmlUrl: getWorkflowRunUrl(notification), }; } @@ -35,6 +42,10 @@ class WorkflowRunHandler extends DefaultHandler { iconType(_subject: Subject): FC | null { return RocketIcon; } + + defaultUrl(notification: Notification): Link { + return getWorkflowRunUrl(notification); + } } export const workflowRunHandler = new WorkflowRunHandler(); @@ -64,11 +75,25 @@ export function getWorkflowRunAttributes( }; } -function getWorkflowRunStatus(statusDisplayName: string): CheckSuiteStatus { +function getWorkflowRunStatus( + statusDisplayName: string, +): GitifyCheckSuiteStatus { switch (statusDisplayName) { case 'review': - return 'waiting'; + return 'WAITING'; default: return null; } } + +export function getWorkflowRunUrl(notification: Notification): Link { + const filters = []; + + const workflowRunAttributes = getWorkflowRunAttributes(notification); + + if (workflowRunAttributes?.status) { + filters.push(`is:${workflowRunAttributes.status}`); + } + + return actionsURL(notification.repository.html_url, filters); +} diff --git a/src/renderer/utils/notifications/notifications.test.ts b/src/renderer/utils/notifications/notifications.test.ts index e3205c954..7ee1e2c8c 100644 --- a/src/renderer/utils/notifications/notifications.test.ts +++ b/src/renderer/utils/notifications/notifications.test.ts @@ -64,13 +64,15 @@ describe('renderer/utils/notifications/notifications.ts', () => { url: 'https://api.github.com/repos/gitify-app/notifications-test/issues/1' as Link, }); const mockRepository = { + name: 'notifications-test', full_name: 'gitify-app/notifications-test', + owner: { + login: 'gitify-app', + }, } as Repository; mockNotification.repository = mockRepository; - nock('https://api.github.com') - .get('/repos/gitify-app/notifications-test/issues/1') - .replyWithError(mockError); + nock('https://api.github.com').post('/graphql').replyWithError(mockError); await enrichNotification(mockNotification, mockSettings); diff --git a/src/renderer/utils/notifications/notifications.ts b/src/renderer/utils/notifications/notifications.ts index 7780af27c..ff822c872 100644 --- a/src/renderer/utils/notifications/notifications.ts +++ b/src/renderer/utils/notifications/notifications.ts @@ -1,9 +1,10 @@ import type { AccountNotifications, GitifyState, + GitifySubject, SettingsState, } from '../../types'; -import type { GitifySubject, Notification } from '../../typesGitHub'; +import type { Notification } from '../../typesGitHub'; import { listNotificationsForAuthenticatedUser } from '../api/client'; import { determineFailureType } from '../api/errors'; import { rendererLogError, rendererLogWarn } from '../logger';