diff --git a/services/github/pod-github/src/sync/comments.ts b/services/github/pod-github/src/sync/comments.ts index c435fa41f22..b64b290a756 100644 --- a/services/github/pod-github/src/sync/comments.ts +++ b/services/github/pod-github/src/sync/comments.ts @@ -24,7 +24,7 @@ import { githubExternalSyncVersion, githubSyncVersion } from '../types' -import { collectUpdate, deleteObjects, errorToObj, getSince, isGHWriteAllowed } from './utils' +import { collectUpdate, deleteObjects, ensureGraphQLOctokit, errorToObj, getSince, isGHWriteAllowed } from './utils' import { Analytics } from '@hcengineering/analytics' import { IssueComment, IssueCommentCreatedEvent, IssueCommentEvent } from '@octokit/webhooks-types' @@ -144,7 +144,10 @@ export class CommentSyncManager implements DocSyncManager { account: PersonId, id: string ): Promise { - const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit + const okit = ensureGraphQLOctokit( + (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit, + container + ) const q = `mutation deleteComment($commentID: ID!) { deleteIssueComment( diff --git a/services/github/pod-github/src/sync/issueBase.ts b/services/github/pod-github/src/sync/issueBase.ts index 1cd2872dd01..bf4ce7535fa 100644 --- a/services/github/pod-github/src/sync/issueBase.ts +++ b/services/github/pod-github/src/sync/issueBase.ts @@ -38,7 +38,7 @@ import { Octokit } from 'octokit' import { ContainerFocus, IntegrationManager, githubExternalSyncVersion, githubSyncVersion } from '../types' import { IssueExternalData } from './githubTypes' import { stripGuestLink } from './guest' -import { collectUpdate, compareMarkdown, deleteObjects, errorToObj, guessStatus } from './utils' +import { collectUpdate, compareMarkdown, deleteObjects, ensureGraphQLOctokit, errorToObj, guessStatus } from './utils' /** * @public @@ -338,7 +338,10 @@ export abstract class IssueSyncManagerBase { const allAttributes = this.client.getHierarchy().getAllAttributes(existingIssue._class) const platformUpdate = collectUpdate(previousData, existingIssue, Array.from(allAttributes.keys())) - const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit + const okit = ensureGraphQLOctokit( + (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit, + container + ) // Remove current same values from update for (const [k, v] of Object.entries(update)) { diff --git a/services/github/pod-github/src/sync/issues.ts b/services/github/pod-github/src/sync/issues.ts index d5faa06234c..200b74c4458 100644 --- a/services/github/pod-github/src/sync/issues.ts +++ b/services/github/pod-github/src/sync/issues.ts @@ -48,7 +48,7 @@ import { } from '../types' import { IssueExternalData, issueDetails } from './githubTypes' import { GithubIssueData, IssueSyncManagerBase, IssueUpdate, WithMarkup } from './issueBase' -import { getSince, gqlp, guessStatus, isGHWriteAllowed, syncRunner } from './utils' +import { ensureGraphQLOctokit, getSince, gqlp, guessStatus, isGHWriteAllowed, syncRunner } from './utils' export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncManager { createPromise: Promise | undefined @@ -608,6 +608,8 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan okit: Octokit, account: PersonId ): Promise { + const graphqlOkit = ensureGraphQLOctokit(okit, container) + const { state, stateReason, body, ...issueUpdate } = await this.collectIssueUpdate( info, existing, @@ -624,7 +626,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan // We should allow modification from user. const closeIssue = async (): Promise => { - await okit.graphql( + await graphqlOkit.graphql( ` mutation closeIssue($issue: ID!) { closeIssue(input: { @@ -642,7 +644,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan } const reopenIssue = async (): Promise => { - await okit.graphql( + await graphqlOkit.graphql( ` mutation reopenIssue($issue: ID!) { reopenIssue(input: { @@ -675,7 +677,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan // We need to call re-open issue await reopenIssue() } - await okit.graphql( + await graphqlOkit.graphql( ` mutation updateIssue($issue: ID!, $body: String! ) { updateIssue(input: { @@ -713,7 +715,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan await reopenIssue() } if (hasOtherChanges) { - await okit.graphql( + await graphqlOkit.graphql( ` mutation updateIssue($issue: ID!) { updateIssue(input: { @@ -751,7 +753,10 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan ): Promise { const existingIssue = existing - const okit = (await this.provider.getOctokit(ctx, existingIssue.modifiedBy)) ?? container.container.octokit + const okit = ensureGraphQLOctokit( + (await this.provider.getOctokit(ctx, existingIssue.modifiedBy)) ?? container.container.octokit, + container + ) const repoId = repository.nodeId @@ -797,7 +802,10 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan account: PersonId, id: string ): Promise { - const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit + const okit = ensureGraphQLOctokit( + (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit, + container + ) const q = `mutation deleteIssue($issueID: ID!) { deleteIssue( diff --git a/services/github/pod-github/src/sync/pullrequests.ts b/services/github/pod-github/src/sync/pullrequests.ts index 11bff84de4e..97df74b3f1b 100644 --- a/services/github/pod-github/src/sync/pullrequests.ts +++ b/services/github/pod-github/src/sync/pullrequests.ts @@ -61,6 +61,7 @@ import { } from './githubTypes' import { GithubIssueData, IssueSyncManagerBase, WithMarkup } from './issueBase' import { + ensureGraphQLOctokit, errorToObj, getSinceRaw, gqlp, @@ -975,6 +976,8 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS okit: Octokit, account: PersonId ): Promise { + const graphqlOkit = ensureGraphQLOctokit(okit, container) + let { state, stateReason, body, ...issueUpdate } = await this.collectIssueUpdate( info, existing, @@ -1006,7 +1009,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS workspace: this.provider.getWorkspaceId() }) if (isGHWriteAllowed()) { - await okit.graphql( + await graphqlOkit.graphql( ` mutation updatePullRequest($issue: ID!, $body: String!) { updatePullRequest(input: { @@ -1040,7 +1043,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS workspace: this.provider.getWorkspaceId() }) if (isGHWriteAllowed()) { - await okit.graphql( + await graphqlOkit.graphql( ` mutation updatePullRequest($issue: ID!) { updatePullRequest(input: { diff --git a/services/github/pod-github/src/sync/reviewComments.ts b/services/github/pod-github/src/sync/reviewComments.ts index c1c4a378d46..d4214d6cf67 100644 --- a/services/github/pod-github/src/sync/reviewComments.ts +++ b/services/github/pod-github/src/sync/reviewComments.ts @@ -29,7 +29,7 @@ import { githubSyncVersion } from '../types' import { ReviewComment as ReviewCommentExternalData, reviewCommentDetails } from './githubTypes' -import { collectUpdate, deleteObjects, errorToObj, isGHWriteAllowed } from './utils' +import { collectUpdate, deleteObjects, ensureGraphQLOctokit, errorToObj, isGHWriteAllowed } from './utils' import { Analytics } from '@hcengineering/analytics' import { PullRequestReviewCommentCreatedEvent, PullRequestReviewCommentEvent } from '@octokit/webhooks-types' @@ -155,7 +155,10 @@ export class ReviewCommentSyncManager implements DocSyncManager { derivedClient: TxOperations, parent?: DocSyncInfo ): Promise { - const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit + const okit = ensureGraphQLOctokit( + (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit, + container + ) const q = `mutation deleteReviewComment($reviewID: ID!) { deletePullRequestReviewComment(input: { id: $reviewID @@ -435,7 +438,10 @@ export class ReviewCommentSyncManager implements DocSyncManager { if (Object.keys(platformUpdate).length > 0) { if (platformUpdate.body !== undefined) { const body = await this.provider.getMarkupSafe(container.container, platformUpdate.body) - const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit + const okit = ensureGraphQLOctokit( + (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit, + container + ) const q = `mutation updateReviewComment($commentID: ID!, $body: String!) { updatePullRequestReviewComment(input: { threadId: $threadID @@ -510,7 +516,10 @@ export class ReviewCommentSyncManager implements DocSyncManager { return {} } const existingReview = existing as GithubReviewComment - const okit = (await this.provider.getOctokit(ctx, existingReview.modifiedBy)) ?? container.container.octokit + const okit = ensureGraphQLOctokit( + (await this.provider.getOctokit(ctx, existingReview.modifiedBy)) ?? container.container.octokit, + container + ) // No external version yet, create it. try { diff --git a/services/github/pod-github/src/sync/reviewThreads.ts b/services/github/pod-github/src/sync/reviewThreads.ts index fbc48a80e11..bd65b0ec321 100644 --- a/services/github/pod-github/src/sync/reviewThreads.ts +++ b/services/github/pod-github/src/sync/reviewThreads.ts @@ -35,7 +35,15 @@ import { getUpdatedAtReviewThread, reviewThreadDetails } from './githubTypes' -import { collectUpdate, deleteObjects, errorToObj, isGHWriteAllowed, syncChilds, syncDerivedDocuments } from './utils' +import { + collectUpdate, + deleteObjects, + ensureGraphQLOctokit, + errorToObj, + isGHWriteAllowed, + syncChilds, + syncDerivedDocuments +} from './utils' import { Analytics } from '@hcengineering/analytics' import { PullRequestReviewThreadEvent } from '@octokit/webhooks-types' @@ -371,7 +379,10 @@ export class ReviewThreadSyncManager implements DocSyncManager { if (Object.keys(platformUpdate).length > 0) { // Check and update external if (platformUpdate.isResolved !== undefined && githubConfiguration.ResolveThreadSupported) { - const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit + const okit = ensureGraphQLOctokit( + (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit, + container + ) const q = `mutation updateReviewThread($threadID: ID!) { ${platformUpdate.isResolved ? 'resolveReviewThread' : 'unresolveReviewThread'} ( input: { @@ -446,7 +457,10 @@ export class ReviewThreadSyncManager implements DocSyncManager { return {} } const existingReview = existing as GithubReviewThread - const okit = (await this.provider.getOctokit(ctx, existingReview.modifiedBy)) ?? container.container.octokit + const okit = ensureGraphQLOctokit( + (await this.provider.getOctokit(ctx, existingReview.modifiedBy)) ?? container.container.octokit, + container + ) // No external version yet, create it. // Will be added into pending state. diff --git a/services/github/pod-github/src/sync/reviews.ts b/services/github/pod-github/src/sync/reviews.ts index 1c5496b51dd..0519d46145e 100644 --- a/services/github/pod-github/src/sync/reviews.ts +++ b/services/github/pod-github/src/sync/reviews.ts @@ -29,7 +29,7 @@ import { githubSyncVersion } from '../types' import { PullRequestExternalData, Review as ReviewExternalData, reviewDetails, toReviewState } from './githubTypes' -import { collectUpdate, deleteObjects, errorToObj, isGHWriteAllowed, syncChilds } from './utils' +import { collectUpdate, deleteObjects, ensureGraphQLOctokit, errorToObj, isGHWriteAllowed, syncChilds } from './utils' import { Analytics } from '@hcengineering/analytics' import { PullRequestReviewEvent, PullRequestReviewSubmittedEvent } from '@octokit/webhooks-types' @@ -150,7 +150,10 @@ export class ReviewSyncManager implements DocSyncManager { account: PersonId, id: string ): Promise { - const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit + const okit = ensureGraphQLOctokit( + (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit, + container + ) const q = `mutation deleteReview($reviewID: ID!) { deletePullRequestReview(input: { pullRequestReviewId: $reviewID @@ -432,7 +435,10 @@ export class ReviewSyncManager implements DocSyncManager { return {} } const existingReview = existing as GithubReview - const okit = (await this.provider.getOctokit(ctx, existingReview.modifiedBy)) ?? container.container.octokit + const okit = ensureGraphQLOctokit( + (await this.provider.getOctokit(ctx, existingReview.modifiedBy)) ?? container.container.octokit, + container + ) // No external version yet, create it. try { diff --git a/services/github/pod-github/src/sync/utils.ts b/services/github/pod-github/src/sync/utils.ts index 342224b53f3..5feafce4025 100644 --- a/services/github/pod-github/src/sync/utils.ts +++ b/services/github/pod-github/src/sync/utils.ts @@ -23,7 +23,8 @@ import { PlatformError, unknownStatus } from '@hcengineering/platform' import task from '@hcengineering/task' import { IssueStatus } from '@hcengineering/tracker' import { deepEqual } from 'fast-equals' -import { githubExternalSyncVersion } from '../types' +import { Octokit } from 'octokit' +import { ContainerFocus, githubExternalSyncVersion } from '../types' /** * Return if github write operations are allowed. @@ -35,6 +36,20 @@ export function isGHWriteAllowed (): boolean { return true } +/** + * Ensures an Octokit instance has the graphql method available. + * If the provided okit doesn't have graphql, falls back to container.octokit which is guaranteed to have it. + * @param okit - The Octokit instance to check (may be undefined) + * @param container - The ContainerFocus containing the fallback octokit instance + * @returns An Octokit instance with graphql method available + */ +export function ensureGraphQLOctokit (okit: Octokit | undefined, container: ContainerFocus): Octokit { + if (okit !== undefined && typeof (okit as any).graphql === 'function') { + return okit + } + return container.container.octokit +} + /** * @public */