feat: Pass tracing info with websocket (#9627)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev
2025-07-31 22:58:25 +05:00
committed by GitHub
parent fd5354a7d1
commit d32beb8ead
24 changed files with 694 additions and 440 deletions
+44 -21
View File
@@ -9,7 +9,8 @@ import core, {
DocumentUpdate,
MeasureContext,
Ref,
TxOperations
TxOperations,
withContext
} from '@hcengineering/core'
import github, { DocSyncInfo, GithubIntegrationRepository, GithubProject } from '@hcengineering/github'
import { LiveQuery } from '@hcengineering/query'
@@ -43,7 +44,6 @@ export class CommentSyncManager implements DocSyncManager {
externalDerivedSync = false
constructor (
readonly ctx: MeasureContext,
readonly client: TxOperations,
readonly lq: LiveQuery
) {}
@@ -53,10 +53,17 @@ export class CommentSyncManager implements DocSyncManager {
}
eventSync = new Map<string, Promise<void>>()
async handleEvent<T>(integration: IntegrationContainer, derivedClient: TxOperations, evt: T): Promise<void> {
@withContext('comments-handle-event')
async handleEvent<T>(
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
evt: T
): Promise<void> {
await this.createCommentPromise
const event = evt as IssueCommentEvent
this.ctx.info('comments:handleEvent', {
ctx.info('comments:handleEvent', {
action: event.action,
login: event.sender.login,
workspace: this.provider.getWorkspaceId()
@@ -71,13 +78,14 @@ export class CommentSyncManager implements DocSyncManager {
}
await this.eventSync.get(event.issue.url)
const promise = this.processEvent(event, derivedClient, integration)
const promise = this.processEvent(ctx, event, derivedClient, integration)
this.eventSync.set(event.issue.url, promise)
await promise
this.eventSync.delete(event.issue.url)
}
async handleDelete (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
derivedClient: TxOperations,
@@ -99,7 +107,7 @@ export class CommentSyncManager implements DocSyncManager {
if (commentExternal !== undefined) {
try {
await this.deleteGithubDocument(container, account, commentExternal.node_id)
await this.deleteGithubDocument(ctx, container, account, commentExternal.node_id)
} catch (err: any) {
let cnt = false
if (Array.isArray(err.errors)) {
@@ -119,13 +127,18 @@ export class CommentSyncManager implements DocSyncManager {
}
if (existing !== undefined && deleteExisting) {
await deleteObjects(this.ctx, this.client, [existing], account)
await deleteObjects(ctx, this.client, [existing], account)
}
return true
}
async deleteGithubDocument (container: ContainerFocus, account: PersonId, id: string): Promise<void> {
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
async deleteGithubDocument (
ctx: MeasureContext,
container: ContainerFocus,
account: PersonId,
id: string
): Promise<void> {
const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit
const q = `mutation deleteComment($commentID: ID!) {
deleteIssueComment(
@@ -142,13 +155,14 @@ export class CommentSyncManager implements DocSyncManager {
}
private async processEvent (
ctx: MeasureContext,
event: IssueCommentEvent,
derivedClient: TxOperations,
integration: IntegrationContainer
): Promise<void> {
const { repository: repo } = await this.provider.getProjectAndRepository(event.repository.node_id)
if (repo === undefined) {
this.ctx.info('No project for repository', {
ctx.info('No project for repository', {
repository: event.repository,
workspace: this.provider.getWorkspaceId()
})
@@ -233,7 +247,9 @@ export class CommentSyncManager implements DocSyncManager {
}
}
@withContext('comments-sync')
async sync (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
parent: DocSyncInfo | undefined,
@@ -252,7 +268,7 @@ export class CommentSyncManager implements DocSyncManager {
}
// If no external document, we need to create it.
this.createCommentPromise = this.createGithubComment(container, existing, info, parent, derivedClient)
this.createCommentPromise = this.createGithubComment(ctx, container, existing, info, parent, derivedClient)
return await this.createCommentPromise
}
const comment = info.external as CommentExternalData
@@ -279,16 +295,17 @@ export class CommentSyncManager implements DocSyncManager {
return { needSync: githubSyncVersion, current: messageData }
} catch (err: any) {
Analytics.handleError(err)
this.ctx.error(err)
ctx.error(err)
return { needSync: githubSyncVersion, error: errorToObj(err) }
}
} else {
await this.handleDiffUpdate(existing, info, messageData, container, parent, comment, account)
await this.handleDiffUpdate(ctx, existing, info, messageData, container, parent, comment, account)
}
return { current: messageData, needSync: githubSyncVersion }
}
private async handleDiffUpdate (
ctx: MeasureContext,
existing: Doc,
info: DocSyncInfo,
messageData: MessageData,
@@ -327,7 +344,7 @@ export class CommentSyncManager implements DocSyncManager {
if (Object.keys(platformUpdate).length > 0) {
// Check and update body with external
const okit = (await this.provider.getOctokit(existing.modifiedBy)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(ctx, existing.modifiedBy)) ?? container.container.octokit
const mdown = await this.provider.getMarkdown(existingComment.message)
if (mdown.trim().length > 0) {
await okit?.rest.issues.updateComment({
@@ -382,6 +399,7 @@ export class CommentSyncManager implements DocSyncManager {
}
async createGithubComment (
ctx: MeasureContext,
container: ContainerFocus,
existing: Doc | undefined,
info: DocSyncInfo,
@@ -399,7 +417,7 @@ export class CommentSyncManager implements DocSyncManager {
return {}
}
const chatMessage = existing as ChatMessage
const okit = (await this.provider.getOctokit(chatMessage.modifiedBy)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(ctx, chatMessage.modifiedBy)) ?? container.container.octokit
// No external version yet, create it.
try {
@@ -430,12 +448,14 @@ export class CommentSyncManager implements DocSyncManager {
return { needSync: githubSyncVersion }
} catch (err: any) {
Analytics.handleError(err)
this.ctx.error(err)
ctx.error(err)
return { needSync: githubSyncVersion, error: errorToObj(err) }
}
}
@withContext('comments-externalSync')
async externalSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
kind: ExternalSyncField,
@@ -452,11 +472,13 @@ export class CommentSyncManager implements DocSyncManager {
this.provider.sync()
}
repositoryDisabled (integration: IntegrationContainer, repo: GithubIntegrationRepository): void {
repositoryDisabled (ctx: MeasureContext, integration: IntegrationContainer, repo: GithubIntegrationRepository): void {
integration.synchronized.delete(`${repo._id}:comment`)
}
@withContext('comments-externalFullSync')
async externalFullSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
projects: GithubProject[],
@@ -500,25 +522,26 @@ export class CommentSyncManager implements DocSyncManager {
break
}
const comments: CommentExternalData[] = data.data as any
this.ctx.info('retrieve comments for', {
ctx.info('retrieve comments for', {
repo: repo.name,
comments: comments.length,
used: data.headers['x-ratelimit-used'],
limit: data.headers['x-ratelimit-limit'],
workspace: this.provider.getWorkspaceId()
})
await this.syncComments(repo, comments, derivedClient)
await this.syncComments(ctx, repo, comments, derivedClient)
this.provider.sync()
}
} catch (err: any) {
Analytics.handleError(err)
this.ctx.error(err)
ctx.error(err)
}
integration.synchronized.add(syncKey)
}
}
async syncComments (
ctx: MeasureContext,
repo: GithubIntegrationRepository,
comments: CommentExternalData[],
derivedClient: TxOperations
@@ -571,7 +594,7 @@ export class CommentSyncManager implements DocSyncManager {
}
} catch (err: any) {
Analytics.handleError(err)
this.ctx.error(err)
ctx.error(err)
}
}
}
@@ -85,7 +85,6 @@ export type IssueUpdate = DocumentUpdate<WithMarkup<Issue>>
export abstract class IssueSyncManagerBase {
provider!: IntegrationManager
constructor (
readonly ctx: MeasureContext,
readonly client: TxOperations,
readonly lq: LiveQuery,
readonly collaborator: CollaboratorClient
@@ -114,6 +113,7 @@ export abstract class IssueSyncManagerBase {
}
async handleUpdate (
ctx: MeasureContext,
external: IssueExternalData,
derivedClient: TxOperations,
update: IssueUpdate,
@@ -156,7 +156,7 @@ export abstract class IssueSyncManagerBase {
await this.collaborator.updateMarkup(collabId, update.description)
} catch (err: any) {
Analytics.handleError(err)
this.ctx.error(err)
ctx.error(err)
}
} else {
delete update.description
@@ -248,6 +248,7 @@ export abstract class IssueSyncManagerBase {
}
abstract performIssueFieldsUpdate (
ctx: MeasureContext,
info: DocSyncInfo,
existing: WithMarkup<Issue>,
platformUpdate: DocumentUpdate<Issue>,
@@ -258,9 +259,16 @@ export abstract class IssueSyncManagerBase {
account: PersonId
): Promise<boolean>
abstract afterSync (existing: Issue, account: PersonId, issueExternal: any, info: DocSyncInfo): Promise<void>
abstract afterSync (
ctx: MeasureContext,
existing: Issue,
account: PersonId,
issueExternal: any,
info: DocSyncInfo
): Promise<void>
async handleDiffUpdate (
ctx: MeasureContext,
container: ContainerFocus,
existing: WithMarkup<Issue>,
info: DocSyncInfo,
@@ -271,7 +279,7 @@ export abstract class IssueSyncManagerBase {
): Promise<DocumentUpdate<DocSyncInfo>> {
let needUpdate = false
if (!this.client.getHierarchy().hasMixin(existing, github.mixin.GithubIssue)) {
await this.ctx.with(
await ctx.with(
'create mixin issue: GithubIssue',
{},
async () => {
@@ -318,7 +326,7 @@ export abstract class IssueSyncManagerBase {
const allAttributes = this.client.getHierarchy().getAllAttributes(existingIssue._class)
const platformUpdate = collectUpdate<Issue>(previousData, existingIssue, Array.from(allAttributes.keys()))
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit
// Remove current same values from update
for (const [k, v] of Object.entries(update)) {
@@ -343,7 +351,7 @@ export abstract class IssueSyncManagerBase {
}
if (pv != null && pv !== v) {
// We have conflict of values, assume platform is more proper one.
this.ctx.error('conflict', { id: existing.identifier, k })
ctx.error('conflict', { id: existing.identifier, k })
// Assume platform change is more important in case of conflict values.
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete (update as any)[k]
@@ -358,6 +366,7 @@ export abstract class IssueSyncManagerBase {
if (container !== undefined && okit !== undefined) {
// Check and update issue fields.
needExternalSync = await this.performIssueFieldsUpdate(
ctx,
info,
existing,
platformUpdate,
@@ -382,7 +391,7 @@ export abstract class IssueSyncManagerBase {
// Update collaborative description
if (update.description !== undefined) {
this.ctx.info(`<= perform ${issueExternal.url} update to collaborator`, {
ctx.info(`<= perform ${issueExternal.url} update to collaborator`, {
workspace: this.provider.getWorkspaceId()
})
try {
@@ -392,21 +401,21 @@ export abstract class IssueSyncManagerBase {
await this.collaborator.updateMarkup(collabId, description)
} catch (err: any) {
Analytics.handleError(err)
this.ctx.error('error during description update', err)
ctx.error('error during description update', err)
}
delete update.description
}
if (Object.keys(update).length > 0) {
// We have some fields to update of existing from external
this.ctx.info(`<= perform ${issueExternal.url} update to platform`, {
ctx.info(`<= perform ${issueExternal.url} update to platform`, {
...update,
workspace: this.provider.getWorkspaceId()
})
await this.client.update(existingIssue, update, false, new Date().getTime(), accountGH)
}
await this.afterSync(existingIssue, accountGH, issueExternal, info)
await this.afterSync(ctx, existingIssue, accountGH, issueExternal, info)
// We need to trigger external version retrieval, via sync or event, to prevent move sync operations from platform before we will be sure all is updated on github.
return {
current: issueData,
@@ -564,11 +573,12 @@ export abstract class IssueSyncManagerBase {
update.assignee = assignees?.[0] ?? null
}
if (Object.keys(update).length > 0) {
await this.handleUpdate(issueExternal, derivedClient, update, account, container.project, false)
await this.handleUpdate(ctx, issueExternal, derivedClient, update, account, container.project, false)
}
}
async syncIssues (
ctx: MeasureContext,
_class: Ref<Class<Doc>>,
repo: GithubIntegrationRepository,
issues: IssueExternalData[],
@@ -592,14 +602,14 @@ export abstract class IssueSyncManagerBase {
for (const issue of issues) {
try {
if (issue.url === undefined && Object.keys(issue).length === 0) {
this.ctx.info('Retrieve empty document', { repo: repo.name, workspace: this.provider.getWorkspaceId() })
ctx.info('Retrieve empty document', { repo: repo.name, workspace: this.provider.getWorkspaceId() })
continue
}
const existing =
syncInfo.find((it) => it.url.toLowerCase() === issue.url.toLowerCase()) ??
syncInfo.find((it) => (it.external as IssueExternalData)?.id === issue.id)
if (existing === undefined && syncDocs === undefined) {
this.ctx.info('Create sync doc', { url: issue.url, workspace: this.provider.getWorkspaceId() })
ctx.info('Create sync doc', { url: issue.url, workspace: this.provider.getWorkspaceId() })
await ops.createDoc<DocSyncInfo>(github.class.DocSyncInfo, repo.githubProject, {
url: issue.url.toLowerCase(),
needSync: '',
@@ -618,7 +628,7 @@ export abstract class IssueSyncManagerBase {
}
const externalEqual = deepEqual(existing.external, issue) && existing.repository === repo._id
if (!externalEqual || existing.externalVersion !== githubExternalSyncVersion) {
this.ctx.info('Update sync doc(extarnal changes)', {
ctx.info('Update sync doc(extarnal changes)', {
url: issue.url,
workspace: this.provider.getWorkspaceId()
})
@@ -626,7 +636,7 @@ export abstract class IssueSyncManagerBase {
if (existing.needSync === githubSyncVersion || existing.repository !== repo._id) {
// Sync external if and only if no changes from platform or we do resync from github.
// We need to apply changes from Github, while service was offline.
await this.performDocumentExternalSync(this.ctx, existing, existing.external, issue, derivedClient)
await this.performDocumentExternalSync(ctx, existing, existing.external, issue, derivedClient)
}
await ops.diffUpdate(
@@ -646,7 +656,7 @@ export abstract class IssueSyncManagerBase {
}
} catch (err: any) {
Analytics.handleError(err)
this.ctx.error(err)
ctx.error(err)
}
}
// if no sync doc, mark it as synchronized
@@ -661,9 +671,15 @@ export abstract class IssueSyncManagerBase {
this.provider.sync()
}
abstract deleteGithubDocument (container: ContainerFocus, account: PersonId, id: string): Promise<void>
abstract deleteGithubDocument (
ctx: MeasureContext,
container: ContainerFocus,
account: PersonId,
id: string
): Promise<void>
async handleDelete (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
derivedClient: TxOperations,
@@ -684,7 +700,7 @@ export abstract class IssueSyncManagerBase {
if (issueExternal !== undefined) {
try {
await this.deleteGithubDocument(container, account, issueExternal.id)
await this.deleteGithubDocument(ctx, container, account, issueExternal.id)
} catch (err: any) {
let cnt = false
if (Array.isArray(err.errors)) {
@@ -698,7 +714,7 @@ export abstract class IssueSyncManagerBase {
}
if (!cnt) {
Analytics.handleError(err)
this.ctx.error('Error', { err })
ctx.error('Error', { err })
await derivedClient.update(info, { error: errorToObj(err), needSync: githubSyncVersion })
return false
}
@@ -714,7 +730,7 @@ export abstract class IssueSyncManagerBase {
await derivedClient.remove(u)
}
await deleteObjects(this.ctx, this.client, [existing], account)
await deleteObjects(ctx, this.client, [existing], account)
}
return true
}
+76 -44
View File
@@ -8,10 +8,10 @@
*/
import { Analytics } from '@hcengineering/analytics'
import core, {
PersonId,
AttachedData,
Doc,
DocumentUpdate,
PersonId,
Ref,
SortingOrder,
Status,
@@ -20,7 +20,9 @@ import core, {
generateId,
makeCollabId,
makeCollabJsonId,
makeDocCollabId
makeDocCollabId,
withContext,
type MeasureContext
} from '@hcengineering/core'
import github, {
DocSyncInfo,
@@ -64,7 +66,9 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
return assignees
}
@withContext('issues-handleEvent')
async handleEvent<T = IssuesEvent | ProjectsV2ItemEvent>(
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
evt: T
@@ -72,7 +76,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
await this.createPromise
const event = evt as IssuesEvent | ProjectsV2ItemEvent
this.ctx.info('issue:handleEvent', {
ctx.info('issue:handleEvent', {
nodeId: (event as IssuesEvent).issue?.html_url ?? (event as ProjectsV2ItemEvent)?.projects_v2_item.node_id,
action: event.action,
login: event.sender.login,
@@ -96,7 +100,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
const issueEvent = event as IssuesEvent
const { project, repository } = await this.provider.getProjectAndRepository(issueEvent.repository.node_id)
if (project === undefined || repository === undefined) {
this.ctx.info('No project for repository', {
ctx.info('No project for repository', {
repository: issueEvent.repository.name,
nodeId: issueEvent.repository.node_id,
workspace: this.provider.getWorkspaceId()
@@ -107,12 +111,13 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
const urlId = issueEvent.issue.url
await syncRunner.exec(urlId, async () => {
await this.processEvent(issueEvent, derivedClient, repository, integration, project)
await this.processEvent(ctx, issueEvent, derivedClient, repository, integration, project)
})
}
}
private async processEvent (
ctx: MeasureContext,
event: IssuesEvent,
derivedClient: TxOperations,
repo: GithubIntegrationRepository,
@@ -142,7 +147,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
externalData = response.repository.issue
} catch (err: any) {
Analytics.handleError(err)
this.ctx.error('Error', { err })
ctx.error('Error', { err })
// We need to check if we do not have sync data, we need to create by html_url
await this.createErrorSyncDataByUrl(
@@ -197,6 +202,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
await this.handleUpdate(
ctx,
externalData as IssueExternalData,
derivedClient,
update,
@@ -216,7 +222,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
const update: IssueUpdate = {
assignee: persons?.[0] ?? null
}
await this.handleUpdate(externalData as IssueExternalData, derivedClient, update, account, prj, false)
await this.handleUpdate(ctx, externalData as IssueExternalData, derivedClient, update, account, prj, false)
break
}
case 'closed':
@@ -241,6 +247,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
)._id
}
await this.handleUpdate(
ctx,
externalData as IssueExternalData,
derivedClient,
update,
@@ -294,7 +301,9 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
}
@withContext('issues-sync')
async sync (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
parent: DocSyncInfo | undefined,
@@ -317,7 +326,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
if (info.repository == null) {
// No need to sync if component it not yet set
this.ctx.error('Not syncing repository === null', {
ctx.error('Not syncing repository === null', {
url: info.url,
identifier: (existing as Issue).identifier
})
@@ -332,14 +341,14 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
if (info.external === undefined && existing !== undefined) {
const repository = await this.provider.getRepositoryById(info.repository)
if (repository === undefined) {
this.ctx.error('Not syncing repository === undefined', {
ctx.error('Not syncing repository === undefined', {
url: info.url,
identifier: (existing as Issue).identifier
})
return { needSync: githubSyncVersion }
}
const description = await this.ctx.with(
const description = await ctx.with(
'query collaborative description',
{},
async () => {
@@ -350,23 +359,28 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
{ log: true }
)
this.ctx.info('create github issue', {
ctx.info('create github issue', {
title: (existing as Issue).title,
number: (existing as Issue).number,
workspace: this.provider.getWorkspaceId()
})
const createdIssueData = await this.ctx.with(
const createdIssueData = await ctx.with(
'create github issue',
{},
async () => {
this.createPromise = this.createGithubIssue(container, { ...(existing as Issue), description }, repository)
this.createPromise = this.createGithubIssue(
ctx,
container,
{ ...(existing as Issue), description },
repository
)
return await this.createPromise
},
{ id: (existing as Issue).identifier, workspace: this.provider.getWorkspaceId() },
{ log: true }
)
if (createdIssueData === undefined) {
this.ctx.error('Error create issue', { url: info.url })
ctx.error('Error create issue', { url: info.url })
return { needSync: githubSyncVersion, error: 'Unknown error on create issue' }
}
issueExternal = createdIssueData
@@ -397,7 +411,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
return { needSync: githubSyncVersion }
}
const syncResult = await this.syncToTarget(container, existing, issueExternal, derivedClient, info)
const syncResult = await this.syncToTarget(ctx, container, existing, issueExternal, derivedClient, info)
if (externalWasCreated && existing !== undefined) {
// Create child documents
@@ -412,7 +426,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
break
}
await this.provider.doSyncFor(attachedDocs, container.project)
await this.provider.doSyncFor(ctx, attachedDocs, container.project)
for (const child of attachedDocs) {
await derivedClient.update(child, { createId })
}
@@ -430,6 +444,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
async syncToTarget (
ctx: MeasureContext,
container: ContainerFocus,
existing: Doc | undefined,
issueExternal: IssueExternalData,
@@ -463,13 +478,13 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
// TODO: Use GithubProject configuration to specify target type for issues
if (taskTypes.length === 0) {
// Missing required task type
this.ctx.error('Missing required task type', { identifier: (existing as Issue)?.identifier })
ctx.error('Missing required task type', { identifier: (existing as Issue)?.identifier })
return { needSync: githubSyncVersion }
}
if (existing === undefined) {
try {
this.ctx.info('create platform issue', {
ctx.info('create platform issue', {
url: issueExternal.url,
title: issueExternal.title,
workspace: this.provider.getWorkspaceId()
@@ -483,7 +498,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
// No repository, it probable deleted
return { needSync: githubSyncVersion }
}
await this.ctx.with(
await ctx.with(
'create platform issue',
{},
async () => {
@@ -521,12 +536,12 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
} catch (err: any) {
Analytics.handleError(err)
this.ctx.error('Error', { err })
ctx.error('Error', { err })
return { needSync: githubSyncVersion, error: JSON.stringify(err) }
}
} else {
try {
const description = await this.ctx.with(
const description = await ctx.with(
'query collaborative description',
{},
async () => {
@@ -537,11 +552,12 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
{ log: true }
)
const updateResult = await this.ctx.with(
const updateResult = await ctx.with(
'diff update',
{},
async () =>
await this.handleDiffUpdate(
ctx,
container,
{ ...(existing as any), description },
info,
@@ -560,15 +576,21 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
} catch (err: any) {
Analytics.handleError(err)
this.ctx.error('error sync', { err })
ctx.error('error sync', { err })
return { needSync: githubSyncVersion, error: JSON.stringify(err), external: issueExternal }
}
}
}
async afterSync (existing: Issue, update: DocumentUpdate<Doc>, account: PersonId): Promise<void> {}
async afterSync (
ctx: MeasureContext,
existing: Issue,
update: DocumentUpdate<Doc>,
account: PersonId
): Promise<void> {}
async performIssueFieldsUpdate (
ctx: MeasureContext,
info: DocSyncInfo,
existing: WithMarkup<Issue>,
platformUpdate: DocumentUpdate<Issue>,
@@ -630,11 +652,11 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
if (hasFieldStateChanges || body !== undefined) {
if (body !== undefined && !isLocked) {
await this.ctx.with(
await ctx.with(
'==> updateIssue',
{},
async () => {
this.ctx.info('update fields', {
ctx.info('update fields', {
url: issueExternal.url,
...issueUpdate,
body,
@@ -671,11 +693,11 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
)
issueData.description = await this.provider.getMarkupSafe(container.container, body, this.stripGuestLink)
} else if (hasFieldStateChanges) {
await this.ctx.with(
await ctx.with(
'==> updateIssue',
{},
async () => {
this.ctx.info('update fields', { ...issueUpdate, workspace: this.provider.getWorkspaceId() })
ctx.info('update fields', { ...issueUpdate, workspace: this.provider.getWorkspaceId() })
if (isGHWriteAllowed()) {
const hasOtherChanges = Object.keys(issueUpdate).length > 0
if (state === 'OPEN') {
@@ -714,13 +736,14 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
async createGithubIssue (
ctx: MeasureContext,
container: ContainerFocus,
existing: WithMarkup<Issue>,
repository: GithubIntegrationRepository
): Promise<IssueExternalData | undefined> {
const existingIssue = existing
const okit = (await this.provider.getOctokit(existingIssue.modifiedBy)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(ctx, existingIssue.modifiedBy)) ?? container.container.octokit
const repoId = repository.nodeId
@@ -760,8 +783,13 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
}
async deleteGithubDocument (container: ContainerFocus, account: PersonId, id: string): Promise<void> {
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
async deleteGithubDocument (
ctx: MeasureContext,
container: ContainerFocus,
account: PersonId,
id: string
): Promise<void> {
const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit
const q = `mutation deleteIssue($issueID: ID!) {
deleteIssue(
@@ -871,7 +899,9 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
async fillBackChanges (update: DocumentUpdate<Issue>, existing: TGithubIssue, external: any): Promise<void> {}
@withContext('issues-externalSync')
async externalSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
kind: ExternalSyncField,
@@ -897,7 +927,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
const idsp = idsPart.map((it) => `"${it}"`).join(', ')
try {
const response: any = await this.ctx.with(
const response: any = await ctx.with(
'graphql.listIssue',
{},
() =>
@@ -919,19 +949,19 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
const issues: IssueExternalData[] = response.nodes
if (issues.some((issue) => issue.url === undefined && Object.keys(issue).length === 0)) {
this.ctx.error('empty document content', {
ctx.error('empty document content', {
repo: repo.name,
workspace: this.provider.getWorkspaceId(),
data: cutObjectArray(response)
})
}
await this.syncIssues(tracker.class.Issue, repo, issues, derivedClient, docsPart)
await this.syncIssues(ctx, tracker.class.Issue, repo, issues, derivedClient, docsPart)
} catch (err: any) {
if (partsize > 1) {
partsize = 1
allSyncDocs.push(...docsPart)
this.ctx.warn('issue external retrieval switch to one by one mode', {
ctx.warn('issue external retrieval switch to one by one mode', {
errors: err.errors,
msg: err.message,
workspace: this.provider.getWorkspaceId()
@@ -940,7 +970,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
// We need to update issue, since it is missing on external side.
const syncDoc = syncDocs.find((it) => it.external.id === idsPart[0])
if (syncDoc !== undefined) {
this.ctx.warn('mark missing external PR', {
ctx.warn('mark missing external PR', {
errors: err.errors,
msg: err.message,
url: syncDoc.url,
@@ -961,7 +991,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
for (const d of syncDocs) {
if ((d.external as IssueExternalData).id == null) {
this.ctx.error('failed to do external sync for', { objectClass: d.objectClass, _id: d._id })
ctx.error('failed to do external sync for', { objectClass: d.objectClass, _id: d._id })
// no external data for doc
await derivedClient.update<DocSyncInfo>(d, {
externalVersion: githubExternalSyncVersion
@@ -971,15 +1001,17 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
this.provider.sync()
} catch (err: any) {
Analytics.handleError(err)
this.ctx.error('Error', { err })
ctx.error('Error', { err })
}
}
repositoryDisabled (integration: IntegrationContainer, repo: GithubIntegrationRepository): void {
repositoryDisabled (ctx: MeasureContext, integration: IntegrationContainer, repo: GithubIntegrationRepository): void {
integration.synchronized.delete(`${repo._id}:issues`)
}
@withContext('issues-externalFullSync')
async externalFullSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
projects: GithubProject[],
@@ -1010,7 +1042,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
const since = await getSince(this.client, tracker.class.Issue, repo)
this.ctx.info('sync external issues', { repo: repo.name, since, workspace: this.provider.getWorkspaceId() })
ctx.info('sync external issues', { repo: repo.name, since, workspace: this.provider.getWorkspaceId() })
const i = integration.octokit.graphql.paginate.iterator(
`query listIssue($name: String!, $owner: String!, $since: DateTime!, $cursor: String) {
@@ -1042,21 +1074,21 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
const issues: IssueExternalData[] = data.repository.issues.nodes
if (issues.some((issue) => issue.url === undefined && Object.keys(issue).length === 0)) {
this.ctx.error('empty document content', {
ctx.error('empty document content', {
repo: repo.name,
workspace: this.provider.getWorkspaceId(),
data: cutObjectArray(data)
})
}
await this.syncIssues(tracker.class.Issue, repo, issues, derivedClient)
await this.syncIssues(ctx, tracker.class.Issue, repo, issues, derivedClient)
this.provider.sync()
}
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
}
this.ctx.info('sync external issues - done', {
ctx.info('sync external issues - done', {
repo: repo.name,
since,
workspace: this.provider.getWorkspaceId()
@@ -17,7 +17,9 @@ import core, {
cutObjectArray,
generateId,
makeCollabId,
makeDocCollabId
makeDocCollabId,
withContext,
type MeasureContext
} from '@hcengineering/core'
import github, {
DocSyncInfo,
@@ -76,7 +78,14 @@ type GithubPullRequestUpdate = DocumentUpdate<WithMarkup<GithubPullRequest>>
export class PullRequestSyncManager extends IssueSyncManagerBase implements DocSyncManager {
externalDerivedSync = true
async handleEvent<T>(integration: IntegrationContainer, derivedClient: TxOperations, evt: T): Promise<void> {
@withContext('pullrequests-handleEvent')
async handleEvent<T>(
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
evt: T
): Promise<void> {
const _event = evt as PullRequestEvent | ProjectsV2ItemEvent
if (_event.sender.type === 'Bot') {
@@ -86,7 +95,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
return
}
}
this.ctx.info('pull request:handleEvent', {
ctx.info('pull request:handleEvent', {
nodeId:
(_event as PullRequestEvent).pull_request?.html_url ??
(_event as ProjectsV2ItemEvent).projects_v2_item?.node_id,
@@ -104,7 +113,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
const { project, repository } = await this.provider.getProjectAndRepository(event.repository.node_id)
if (project === undefined || repository === undefined) {
this.ctx.info('No project for repository', {
ctx.info('No project for repository', {
name: event.repository.name,
workspace: this.provider.getWorkspaceId()
})
@@ -113,12 +122,13 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
const url = event.pull_request.issue_url
await syncRunner.exec(url, async () => {
await this.processEvent(event, derivedClient, repository, integration, project)
await this.processEvent(ctx, event, derivedClient, repository, integration, project)
})
}
}
private async processEvent (
ctx: MeasureContext,
event: PullRequestEvent,
derivedClient: TxOperations,
repo: GithubIntegrationRepository,
@@ -146,7 +156,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
)
externalData = response.repository.pullRequest
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
await this.createErrorSyncDataByUrl(
event.pull_request.html_url,
@@ -192,22 +202,22 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
if (event.changes.base !== undefined) {
update.base = externalData.baseRef
}
await this.handleUpdate(externalData, derivedClient, update, account, prj, false, undefined, undefined, du)
await this.handleUpdate(ctx, externalData, derivedClient, update, account, prj, false, undefined, undefined, du)
break
}
case 'review_requested': {
const update: GithubPullRequestUpdate = {}
await this.handleUpdate(externalData, derivedClient, update, account, prj, true)
await this.handleUpdate(ctx, externalData, derivedClient, update, account, prj, true)
break
}
case 'review_request_removed': {
const update: GithubPullRequestUpdate = {}
await this.handleUpdate(externalData, derivedClient, update, account, prj, true)
await this.handleUpdate(ctx, externalData, derivedClient, update, account, prj, true)
break
}
case 'converted_to_draft':
case 'ready_for_review': {
await this.handleUpdate(externalData, derivedClient, {}, account, prj, true)
await this.handleUpdate(ctx, externalData, derivedClient, {}, account, prj, true)
break
}
case 'assigned':
@@ -216,7 +226,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
const update: GithubPullRequestUpdate = {
assignee: assignees?.[0] ?? null
}
await this.handleUpdate(externalData, derivedClient, update, account, prj, true)
await this.handleUpdate(ctx, externalData, derivedClient, update, account, prj, true)
break
}
case 'closed':
@@ -259,6 +269,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
})
}
await this.handleUpdate(
ctx,
externalData,
derivedClient,
update,
@@ -350,6 +361,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
async syncToTarget (
ctx: MeasureContext,
container: ContainerFocus,
existing: Doc | undefined,
pullRequestExternal: PullRequestExternalData,
@@ -407,7 +419,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
if (taskTypes.length === 0) {
// Missing required task type
this.ctx.error('Missing required task type', { url: pullRequestExternal.url })
ctx.error('Missing required task type', { url: pullRequestExternal.url })
return { needSync: githubSyncVersion }
}
@@ -415,11 +427,12 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
if (existing === undefined) {
try {
await this.ctx.with(
await ctx.with(
'retrieve pull request patch',
{},
() =>
(ctx) =>
this.handlePatch(
ctx,
info,
container,
pullRequestExternal,
@@ -442,10 +455,10 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
let op = this.client.apply()
let createdPullRequest: GithubPullRequest | undefined
await this.ctx.with(
await ctx.with(
'create pull request in platform',
{},
async () => {
async (ctx) => {
createdPullRequest = await this.createPullRequest(
op,
info,
@@ -475,9 +488,9 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
if (pullRequestObj !== undefined) {
op = this.client.apply()
try {
await this.todoSync(op, pullRequestObj, pullRequestExternal, info, account)
await this.todoSync(ctx, op, pullRequestObj, pullRequestExternal, info, account)
} catch (err: any) {
this.ctx.error('failed to sync todos', { err, url: pullRequestExternal.url, id: pullRequestObj._id })
ctx.error('failed to sync todos', { err, url: pullRequestExternal.url, id: pullRequestObj._id })
}
await op.commit()
}
@@ -494,18 +507,19 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
markdown
}
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
return { needSync: githubSyncVersion, error: errorToObj(err) }
}
} else {
try {
if (info.updatePatch === true) {
await this.ctx.with(
await ctx.with(
'update pull request patch',
{},
() =>
(ctx) =>
this.handlePatch(
ctx,
info,
container,
pullRequestExternal,
@@ -522,10 +536,10 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
)
}
const description = await this.ctx.with(
const description = await ctx.with(
'query collaborative pull request description',
{},
async () => {
async (ctx) => {
const collabId = makeDocCollabId(existing, 'description')
return await this.collaborator.getMarkup(collabId, (existing as GithubPullRequest).description)
},
@@ -533,11 +547,12 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
{ log: true }
)
const update = await this.ctx.with(
const update = await ctx.with(
'perform pull request diff update',
{},
() =>
(ctx) =>
this.handleDiffUpdate(
ctx,
container,
{ ...(existing as any), description },
info,
@@ -556,23 +571,30 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
lastGithubAccount: null
}
} catch (err: any) {
this.ctx.error('Error update pr', { err })
ctx.error('Error update pr', { err })
Analytics.handleError(err)
return { needSync: githubSyncVersion, error: errorToObj(err), external: pullRequestExternal }
}
}
}
async afterSync (existing: Issue, account: PersonId, issueExternal: any, info: DocSyncInfo): Promise<void> {
async afterSync (
ctx: MeasureContext,
existing: Issue,
account: PersonId,
issueExternal: any,
info: DocSyncInfo
): Promise<void> {
const pullRequest = existing as GithubPullRequest
try {
await this.todoSync(this.client, pullRequest, issueExternal as PullRequestExternalData, info, account)
await this.todoSync(ctx, this.client, pullRequest, issueExternal as PullRequestExternalData, info, account)
} catch (err: any) {
this.ctx.error('failed to sync todos', { err, url: issueExternal.url, id: pullRequest._id })
ctx.error('failed to sync todos', { err, url: issueExternal.url, id: pullRequest._id })
}
}
async todoSync (
ctx: MeasureContext,
client: TxOperations,
pullRequest: Pick<
GithubPullRequest,
@@ -893,7 +915,9 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
}
@withContext('pullrequests-sync')
async sync (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
parent: DocSyncInfo | undefined,
@@ -915,7 +939,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
return { needSync: '' }
}
const syncResult = await this.syncToTarget(container, existing, pullRequestExternal, derivedClient, info)
const syncResult = await this.syncToTarget(ctx, container, existing, pullRequestExternal, derivedClient, info)
if (existing !== undefined && pullRequestExternal !== undefined && needCreateConnectedAtHuly) {
await this.addHulyLink(info, syncResult, existing, pullRequestExternal, container)
@@ -926,6 +950,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
async performIssueFieldsUpdate (
ctx: MeasureContext,
info: DocSyncInfo,
existing: WithMarkup<Issue>,
platformUpdate: DocumentUpdate<Issue>,
@@ -955,11 +980,11 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
if (hasFieldsUpdate || body !== undefined) {
if (body !== undefined && !isLocked) {
await this.ctx.with(
await ctx.with(
'==> updatePullRequest',
{},
async () => {
this.ctx.info('update-pr-fields', {
async (ctx) => {
ctx.info('update-pr-fields', {
url: issueExternal.url,
...issueUpdate,
body,
@@ -990,11 +1015,11 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
)
issueData.description = await this.provider.getMarkupSafe(container.container, body, this.stripGuestLink)
} else if (hasFieldsUpdate) {
await this.ctx.with(
await ctx.with(
'==> updatePullRequest:',
{},
async () => {
this.ctx.info('update-fields', {
async (ctx) => {
ctx.info('update-fields', {
url: issueExternal.url,
...issueUpdate,
workspace: this.provider.getWorkspaceId()
@@ -1028,6 +1053,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
private async handlePatch (
ctx: MeasureContext,
info: DocSyncInfo,
container: ContainerFocus,
pullRequestExternal: PullRequestExternalData,
@@ -1040,7 +1066,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
return
}
if (info.external?.patch !== true) {
const { patch, contentType } = await this.fetchPatch(pullRequestExternal, container.container.octokit, repo)
const { patch, contentType } = await this.fetchPatch(ctx, pullRequestExternal, container.container.octokit, repo)
// Update attached patch data.
const patchAttachment = await this.client.findOne(github.class.GithubPatch, { attachedTo: existingPR._id })
@@ -1189,7 +1215,9 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
}
@withContext('pullrequests-externalSync')
async externalSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
kind: ExternalSyncField,
@@ -1200,7 +1228,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
if (kind === 'externalVersion') {
// Bulk update of selected PR's
// Wait global project sync
await this.performExternalSync(integration, prj, syncDocs, repo, derivedClient)
await this.performExternalSync(ctx, integration, prj, syncDocs, repo, derivedClient)
}
if (kind === 'derivedVersion') {
@@ -1258,6 +1286,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
private async performExternalSync (
ctx: MeasureContext,
integration: IntegrationContainer,
prj: GithubProject,
syncDocs: DocSyncInfo[],
@@ -1278,10 +1307,10 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
const idsp = idsPart.map((it) => `"${it}"`).join(', ')
try {
const response: any = await this.ctx.with(
const response: any = await ctx.with(
'fetch pull request updates',
{},
async () =>
async (ctx) =>
await integration.octokit.graphql(
`query listIssues {
nodes(ids: [${idsp}] ) {
@@ -1301,18 +1330,18 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
const issues: PullRequestExternalData[] = response.nodes
if (issues.some((issue) => issue.url === undefined && Object.keys(issue).length === 0)) {
this.ctx.error('empty document content updates', {
ctx.error('empty document content updates', {
repo: repo.name,
workspace: this.provider.getWorkspaceId(),
data: cutObjectArray(response)
})
}
await this.syncIssues(github.class.GithubPullRequest, repo, issues, derivedClient, docsPart)
await this.syncIssues(ctx, github.class.GithubPullRequest, repo, issues, derivedClient, docsPart)
} catch (err: any) {
if (partsize > 1) {
partsize = 1
allSyncDocs.push(...docsPart)
this.ctx.warn('pull request external retrieval switch to one by one mode', {
ctx.warn('pull request external retrieval switch to one by one mode', {
errors: err.errors,
msg: err.message,
workspace: this.provider.getWorkspaceId()
@@ -1321,7 +1350,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
// We need to update issue, since it is missing on external side.
const syncDoc = syncDocs.find((it) => it.external.id === idsPart[0])
if (syncDoc !== undefined) {
this.ctx.warn('mark missing external PR', {
ctx.warn('mark missing external PR', {
errors: err.errors,
msg: err.message,
url: syncDoc.url,
@@ -1342,7 +1371,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
for (const d of syncDocs) {
if ((d.external as IssueExternalData).id == null) {
this.ctx.error('failed to do external sync for', { objectClass: d.objectClass, _id: d._id })
ctx.error('failed to do external sync for', { objectClass: d.objectClass, _id: d._id })
// no external data for doc
await derivedClient.update<DocSyncInfo>(d, {
externalVersion: githubExternalSyncVersion
@@ -1350,17 +1379,19 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
}
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
}
this.provider.sync()
}
repositoryDisabled (integration: IntegrationContainer, repo: GithubIntegrationRepository): void {
repositoryDisabled (ctx: MeasureContext, integration: IntegrationContainer, repo: GithubIntegrationRepository): void {
integration.synchronized.delete(`${repo._id}:pullRequests`)
}
@withContext('pullrequests-externalFullSync')
async externalFullSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
projects: GithubProject[],
@@ -1392,23 +1423,23 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
const since = await getSinceRaw(this.client, github.class.GithubPullRequest, repo)
// We need always sync open PRs, since review changes are not included into PR updated state.
this.ctx.info('sync external pull requests', {
ctx.info('sync external pull requests', {
repo: repo.name,
since,
workspace: this.provider.getWorkspaceId(),
state: 'OPEN'
})
await this.performPRSync(integration, repo, 'OPEN', undefined, derivedClient, prj)
await this.performPRSync(ctx, integration, repo, 'OPEN', undefined, derivedClient, prj)
this.ctx.info('sync external pull requests', {
ctx.info('sync external pull requests', {
repo: repo.name,
since,
workspace: this.provider.getWorkspaceId(),
state: 'CLOSED, MERGED'
})
await this.performPRSync(integration, repo, 'CLOSED, MERGED', since, derivedClient, prj)
await this.performPRSync(ctx, integration, repo, 'CLOSED, MERGED', since, derivedClient, prj)
this.ctx.info('sync external pull requests - done', {
ctx.info('sync external pull requests - done', {
repo: repo.name,
since,
workspace: this.provider.getWorkspaceId()
@@ -1420,6 +1451,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
private async performPRSync (
ctx: MeasureContext,
integration: IntegrationContainer,
repo: GithubIntegrationRepository,
states: string,
@@ -1459,7 +1491,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
break
}
const issues: PullRequestExternalData[] = data.repository.pullRequests.nodes
this.ctx.info('retrieve pull requests for', {
ctx.info('retrieve pull requests for', {
repo: repo.name,
since,
len: issues.length,
@@ -1478,7 +1510,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
let emptyIndex = -1
emptyIndex = issues.findIndex((issue) => issue.url === undefined && Object.keys(issue).length === 0)
if (emptyIndex !== -1) {
this.ctx.error('empty document content', {
ctx.error('empty document content', {
repo: repo.name,
workspace: this.provider.getWorkspaceId(),
data: cutObjectArray(data),
@@ -1487,15 +1519,16 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
})
}
await this.syncIssues(github.class.GithubPullRequest, repo, issues, derivedClient)
await this.syncIssues(ctx, github.class.GithubPullRequest, repo, issues, derivedClient)
}
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
}
}
async fetchPatch (
ctx: MeasureContext,
pullRequest: PullRequestExternalData,
octokit: Octokit,
repository: GithubIntegrationRepository
@@ -1515,13 +1548,18 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
patch = (patchContent.data as unknown as string) ?? ''
contentType = patchContent.headers['content-type'] ?? 'application/vnd.github.VERSION.diff'
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
}
return { patch, contentType }
}
async deleteGithubDocument (container: ContainerFocus, account: PersonId, id: string): Promise<void> {
async deleteGithubDocument (
ctx: MeasureContext,
container: ContainerFocus,
account: PersonId,
id: string
): Promise<void> {
// No delete is allowed for pull requests
}
}
@@ -3,7 +3,15 @@
//
//
import core, { Doc, DocData, DocumentUpdate, MeasureContext, TxOperations, generateId } from '@hcengineering/core'
import core, {
Doc,
DocData,
DocumentUpdate,
MeasureContext,
TxOperations,
generateId,
withContext
} from '@hcengineering/core'
import github, { DocSyncInfo, GithubIntegrationRepository, GithubProject } from '@hcengineering/github'
import { Endpoints } from '@octokit/types'
import {
@@ -20,7 +28,6 @@ const syncReposKey = 'repo_sync'
export class RepositorySyncMapper implements DocSyncManager {
constructor (
private readonly ctx: MeasureContext,
private readonly client: TxOperations,
private readonly app: App
) {}
@@ -35,11 +42,18 @@ export class RepositorySyncMapper implements DocSyncManager {
}
// Perform synchronization of document with external source.
async sync (existing: Doc | undefined, info: DocSyncInfo): Promise<DocumentUpdate<DocSyncInfo> | undefined> {
@withContext('repository-sync')
async sync (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo
): Promise<DocumentUpdate<DocSyncInfo> | undefined> {
return {}
}
async reloadRepositories (
ctx: MeasureContext,
integration: IntegrationContainer,
repositories?: InstallationCreatedEvent['repositories'] | InstallationUnsuspendEvent['repositories']
): Promise<void> {
@@ -93,7 +107,7 @@ export class RepositorySyncMapper implements DocSyncManager {
Date.now(),
integration.integration.createdBy
)
this.ctx.info('Creating repository info document...', {
ctx.info('Creating repository info document...', {
url: repository.full_name,
workspace: this.provider.getWorkspaceId()
})
@@ -102,7 +116,13 @@ export class RepositorySyncMapper implements DocSyncManager {
}
}
async handleEvent<T>(integration: IntegrationContainer, derivedClient: TxOperations, evt: T): Promise<void> {
@withContext('repository-handleEvent')
async handleEvent<T>(
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
evt: T
): Promise<void> {
const event = evt as RepositoryEvent
const account = (await this.provider.getAccountU(event.sender)) ?? core.account.System
@@ -124,7 +144,7 @@ export class RepositorySyncMapper implements DocSyncManager {
Date.now(),
account
)
this.ctx.info('Creating repository info document...', {
ctx.info('Creating repository info document...', {
url: event.repository.url,
workspace: this.provider.getWorkspaceId()
})
@@ -148,7 +168,7 @@ export class RepositorySyncMapper implements DocSyncManager {
const allProjects = await this.client.findAll(github.mixin.GithubProject, { repositories: githubRepo?._id })
for (const prj of allProjects) {
// We need to force sync
await this.handleRepoRename(integration, prj, githubRepo)
await this.handleRepoRename(ctx, integration, prj, githubRepo)
}
}
@@ -178,6 +198,7 @@ export class RepositorySyncMapper implements DocSyncManager {
}
async handleDelete (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
derivedClient: TxOperations,
@@ -225,7 +246,9 @@ export class RepositorySyncMapper implements DocSyncManager {
}
}
@withContext('repository-externalSync')
async externalSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
kind: ExternalSyncField,
@@ -234,9 +257,11 @@ export class RepositorySyncMapper implements DocSyncManager {
prj: GithubProject
): Promise<void> {}
repositoryDisabled (integration: IntegrationContainer, repo: GithubIntegrationRepository): void {}
repositoryDisabled (ctx: MeasureContext, integration: IntegrationContainer, repo: GithubIntegrationRepository): void {}
@withContext('repository-externalFullSync')
async externalFullSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
projects: GithubProject[],
@@ -244,14 +269,14 @@ export class RepositorySyncMapper implements DocSyncManager {
): Promise<void> {
const inst = integration.octokit
if (inst === undefined || integration.octokit === undefined) {
this.ctx.info('no installation found', { workspace: this.provider.getWorkspaceId() })
ctx.info('no installation found', { workspace: this.provider.getWorkspaceId() })
return
}
if (integration.synchronized.has(syncReposKey)) {
return
}
this.ctx.info('Checking github installation repositories...', {
ctx.info('Checking github installation repositories...', {
installationId: integration.installationId,
workspace: this.provider.getWorkspaceId()
})
@@ -297,7 +322,7 @@ export class RepositorySyncMapper implements DocSyncManager {
Date.now(),
integration.integration.createdBy
)
this.ctx.info('Creating repository info document...', {
ctx.info('Creating repository info document...', {
url: repository.url,
workspace: this.provider.getWorkspaceId()
})
@@ -312,7 +337,7 @@ export class RepositorySyncMapper implements DocSyncManager {
['name', ...Object.keys(rdata)]
)
if (Object.keys(diff).length > 0) {
this.ctx.info('processing repository diff update...', {
ctx.info('processing repository diff update...', {
repository: repository.name,
...diff,
workspace: this.provider.getWorkspaceId()
@@ -343,6 +368,7 @@ export class RepositorySyncMapper implements DocSyncManager {
// Perform a synchronization of a single repository.
async handleRepoRename (
ctx: MeasureContext,
integration: IntegrationContainer,
prj: GithubProject,
repo: GithubIntegrationRepository
@@ -360,7 +386,7 @@ export class RepositorySyncMapper implements DocSyncManager {
"https://api.github.com/repos/hcengineering/anticrm/issues/comments/1679316918"
"https://github.com/hcengineering/uberflow/pull/195"
* */
this.ctx.info('handle repository rename', { repo, workspace: this.provider.getWorkspaceId() })
ctx.info('handle repository rename', { repo, workspace: this.provider.getWorkspaceId() })
const update = async (): Promise<void> => {
while (true) {
const docs = await this.client.findAll(
@@ -9,7 +9,8 @@ import core, {
DocumentUpdate,
MeasureContext,
Ref,
TxOperations
TxOperations,
withContext
} from '@hcengineering/core'
import github, {
DocSyncInfo,
@@ -44,7 +45,6 @@ export class ReviewCommentSyncManager implements DocSyncManager {
externalDerivedSync = false
constructor (
readonly ctx: MeasureContext,
readonly client: TxOperations,
readonly lq: LiveQuery
) {}
@@ -54,7 +54,14 @@ export class ReviewCommentSyncManager implements DocSyncManager {
}
eventSync = new Map<string, Promise<void>>()
async handleEvent<T>(integration: IntegrationContainer, derivedClient: TxOperations, evt: T): Promise<void> {
@withContext('review-comments-handleEvent')
async handleEvent<T>(
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
evt: T
): Promise<void> {
await this.createCommentPromise
const event = evt as PullRequestReviewCommentEvent
@@ -65,27 +72,28 @@ export class ReviewCommentSyncManager implements DocSyncManager {
return
}
}
this.ctx.info('reviewComments:handleEvent', {
ctx.info('reviewComments:handleEvent', {
action: event.action,
login: event.sender.login,
workspace: this.provider.getWorkspaceId()
})
const { project, repository } = await this.provider.getProjectAndRepository(event.repository.node_id)
if (project === undefined || repository === undefined) {
this.ctx.info('No project for repository', {
ctx.info('No project for repository', {
name: event.repository.name,
workspace: this.provider.getWorkspaceId()
})
return
}
await this.eventSync.get(event.comment.html_url)
const promise = this.processEvent(event, derivedClient, repository, integration)
const promise = this.processEvent(ctx, event, derivedClient, repository, integration)
this.eventSync.set(event.comment.html_url, promise)
await promise
this.eventSync.delete(event.comment.html_url)
}
async handleDelete (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
derivedClient: TxOperations,
@@ -108,7 +116,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
if (commentExternal !== undefined) {
try {
await this.deleteGithubDocument(container, account, commentExternal.node_id, derivedClient, parent)
await this.deleteGithubDocument(ctx, container, account, commentExternal.node_id, derivedClient, parent)
} catch (err: any) {
let cnt = false
if (Array.isArray(err.errors)) {
@@ -121,7 +129,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
}
}
if (!cnt) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
await derivedClient.update(info, { error: errorToObj(err) })
}
@@ -129,19 +137,20 @@ export class ReviewCommentSyncManager implements DocSyncManager {
}
if (existing !== undefined && deleteExisting) {
await deleteObjects(this.ctx, this.client, [existing], account)
await deleteObjects(ctx, this.client, [existing], account)
}
return true
}
async deleteGithubDocument (
ctx: MeasureContext,
container: ContainerFocus,
account: PersonId,
id: string,
derivedClient: TxOperations,
parent?: DocSyncInfo
): Promise<void> {
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit
const q = `mutation deleteReviewComment($reviewID: ID!) {
deletePullRequestReviewComment(input: {
id: $reviewID
@@ -163,6 +172,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
}
private async processEvent (
ctx: MeasureContext,
event: PullRequestReviewCommentEvent,
derivedClient: TxOperations,
repo: GithubIntegrationRepository,
@@ -188,7 +198,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
)
externalData = response.node
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
return
}
@@ -292,7 +302,9 @@ export class ReviewCommentSyncManager implements DocSyncManager {
}
}
@withContext('review-comments-sync')
async sync (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
parent: DocSyncInfo | undefined,
@@ -314,7 +326,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
}
// If no external document, we need to create it.
this.createCommentPromise = this.createGithubReviewComment(container, existing, info, parent, derivedClient)
this.createCommentPromise = this.createGithubReviewComment(ctx, container, existing, info, parent, derivedClient)
return await this.createCommentPromise
}
const reviewComment = info.external as ReviewCommentExternalData
@@ -355,17 +367,28 @@ export class ReviewCommentSyncManager implements DocSyncManager {
await this.createReviewComment(info, messageData, parent, reviewComment, account)
return { needSync: githubSyncVersion, current: messageData }
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
return { needSync: githubSyncVersion, error: errorToObj(err) }
}
} else {
await this.handleDiffUpdate(existing, info, messageData, container, parent, reviewComment, account, derivedClient)
await this.handleDiffUpdate(
ctx,
existing,
info,
messageData,
container,
parent,
reviewComment,
account,
derivedClient
)
}
return { current: messageData, needSync: githubSyncVersion }
}
private async handleDiffUpdate (
ctx: MeasureContext,
existing: Doc,
info: DocSyncInfo,
reviewCommentData: ReviewCommentData,
@@ -406,7 +429,7 @@ 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(account)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit
const q = `mutation updateReviewComment($commentID: ID!, $body: String!) {
updatePullRequestReviewComment(input: {
threadId: $threadID
@@ -460,6 +483,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
}
async createGithubReviewComment (
ctx: MeasureContext,
container: ContainerFocus,
existing: Doc | undefined,
info: DocSyncInfo,
@@ -477,7 +501,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
return {}
}
const existingReview = existing as GithubReviewComment
const okit = (await this.provider.getOctokit(existingReview.modifiedBy)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(ctx, existingReview.modifiedBy)) ?? container.container.octokit
// No external version yet, create it.
try {
@@ -540,13 +564,15 @@ export class ReviewCommentSyncManager implements DocSyncManager {
}
return {}
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
return { needSync: githubSyncVersion, error: errorToObj(err) }
}
}
@withContext('review-comments-externalSync')
async externalSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
kind: ExternalSyncField,
@@ -563,9 +589,11 @@ export class ReviewCommentSyncManager implements DocSyncManager {
this.provider.sync()
}
repositoryDisabled (integration: IntegrationContainer, repo: GithubIntegrationRepository): void {}
repositoryDisabled (ctx: MeasureContext, integration: IntegrationContainer, repo: GithubIntegrationRepository): void {}
@withContext('review-comments-externalFullSync')
async externalFullSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
projects: GithubProject[],
@@ -8,7 +8,8 @@ import core, {
DocumentUpdate,
MeasureContext,
Ref,
TxOperations
TxOperations,
withContext
} from '@hcengineering/core'
import github, {
DocSyncInfo,
@@ -66,7 +67,6 @@ export class ReviewThreadSyncManager implements DocSyncManager {
externalDerivedSync = true
constructor (
readonly ctx: MeasureContext,
readonly client: TxOperations,
readonly lq: LiveQuery
) {}
@@ -76,7 +76,14 @@ export class ReviewThreadSyncManager implements DocSyncManager {
}
eventSync = new Map<string, Promise<void>>()
async handleEvent<T>(integration: IntegrationContainer, derivedClient: TxOperations, evt: T): Promise<void> {
@withContext('review-threads-handleEvent')
async handleEvent<T>(
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
evt: T
): Promise<void> {
await this.createCommentPromise
const event = evt as PullRequestReviewThreadEvent
@@ -87,11 +94,11 @@ export class ReviewThreadSyncManager implements DocSyncManager {
return
}
}
this.ctx.info('reviewThreads:handleEvent', { event, workspace: this.provider.getWorkspaceId() })
ctx.info('reviewThreads:handleEvent', { event, workspace: this.provider.getWorkspaceId() })
const { project, repository } = await this.provider.getProjectAndRepository(event.repository.node_id)
if (project === undefined || repository === undefined) {
this.ctx.info('No project for repository', {
ctx.info('No project for repository', {
name: event.repository.name,
workspace: this.provider.getWorkspaceId()
})
@@ -99,13 +106,14 @@ export class ReviewThreadSyncManager implements DocSyncManager {
}
await this.eventSync.get(event.thread.node_id)
const promise = this.processEvent(event, derivedClient, repository, integration)
const promise = this.processEvent(ctx, event, derivedClient, repository, integration)
this.eventSync.set(event.thread.node_id, promise)
await promise
this.eventSync.delete(event.thread.node_id)
}
async handleDelete (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
derivedClient: TxOperations,
@@ -140,7 +148,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
}
}
if (!cnt) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
await derivedClient.update(info, { error: errorToObj(err) })
}
@@ -148,7 +156,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
}
if (existing !== undefined && deleteExisting) {
await deleteObjects(this.ctx, this.client, [existing], account)
await deleteObjects(ctx, this.client, [existing], account)
}
return true
}
@@ -158,6 +166,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
}
private async processEvent (
ctx: MeasureContext,
event: PullRequestReviewThreadEvent,
derivedClient: TxOperations,
repo: GithubIntegrationRepository,
@@ -183,7 +192,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
)
externalData = response.node
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
return
}
@@ -248,7 +257,9 @@ export class ReviewThreadSyncManager implements DocSyncManager {
}
}
@withContext('review-threads-sync')
async sync (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
parent: DocSyncInfo | undefined,
@@ -270,7 +281,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
}
// If no external document, we need to create it.
this.createCommentPromise = this.createGithubReviewThread(container, existing, info, parent, derivedClient)
this.createCommentPromise = this.createGithubReviewThread(ctx, container, existing, info, parent, derivedClient)
return await this.createCommentPromise
}
const review = info.external as ReviewThreadExternalData
@@ -303,17 +314,18 @@ export class ReviewThreadSyncManager implements DocSyncManager {
await syncChilds(info, this.client, derivedClient)
return { needSync: githubSyncVersion, current: messageData }
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
return { needSync: githubSyncVersion, error: errorToObj(err) }
}
} else {
await this.handleDiffUpdate(existing, info, messageData, container, parent, review, account, derivedClient)
await this.handleDiffUpdate(ctx, existing, info, messageData, container, parent, review, account, derivedClient)
}
return { current: messageData, needSync: githubSyncVersion }
}
private async handleDiffUpdate (
ctx: MeasureContext,
existing: Doc,
info: DocSyncInfo,
reviewData: ReviewThreadData,
@@ -354,7 +366,7 @@ 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(account)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit
const q = `mutation updateReviewThread($threadID: ID!) {
${platformUpdate.isResolved ? 'resolveReviewThread' : 'unresolveReviewThread'} (
input: {
@@ -375,7 +387,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
} catch (err: any) {
update.isResolved = !platformUpdate.isResolved
platformUpdate.isResolved = !platformUpdate.isResolved
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
}
await derivedClient.update(info, { external: { ...info.external, isResolved: platformUpdate.isResolved } })
@@ -411,6 +423,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
}
async createGithubReviewThread (
ctx: MeasureContext,
container: ContainerFocus,
existing: Doc | undefined,
info: DocSyncInfo,
@@ -428,7 +441,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
return {}
}
const existingReview = existing as GithubReviewThread
const okit = (await this.provider.getOctokit(existingReview.modifiedBy)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(ctx, existingReview.modifiedBy)) ?? container.container.octokit
// No external version yet, create it.
// Will be added into pending state.
@@ -478,13 +491,15 @@ export class ReviewThreadSyncManager implements DocSyncManager {
}
return {}
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
return { needSync: githubSyncVersion, error: errorToObj(err) }
}
}
@withContext('review-threads-externalSync')
async externalSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
kind: ExternalSyncField,
@@ -550,9 +565,11 @@ export class ReviewThreadSyncManager implements DocSyncManager {
}
}
repositoryDisabled (integration: IntegrationContainer, repo: GithubIntegrationRepository): void {}
repositoryDisabled (ctx: MeasureContext, integration: IntegrationContainer, repo: GithubIntegrationRepository): void {}
@withContext('review-threads-externalFullSync')
async externalFullSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
projects: GithubProject[],
+38 -17
View File
@@ -8,7 +8,8 @@ import core, {
DocumentUpdate,
MeasureContext,
Ref,
TxOperations
TxOperations,
withContext
} from '@hcengineering/core'
import github, {
DocSyncInfo,
@@ -44,7 +45,6 @@ export class ReviewSyncManager implements DocSyncManager {
externalDerivedSync = false
constructor (
readonly ctx: MeasureContext,
readonly client: TxOperations,
readonly lq: LiveQuery
) {}
@@ -54,7 +54,14 @@ export class ReviewSyncManager implements DocSyncManager {
}
eventSync = new Map<string, Promise<void>>()
async handleEvent<T>(integration: IntegrationContainer, derivedClient: TxOperations, evt: T): Promise<void> {
@withContext('reviews-handleEvent')
async handleEvent<T>(
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
evt: T
): Promise<void> {
await this.createCommentPromise
const event = evt as PullRequestReviewEvent
@@ -65,11 +72,11 @@ export class ReviewSyncManager implements DocSyncManager {
return
}
}
this.ctx.info('reviews:handleEvent', { event, workspace: this.provider.getWorkspaceId() })
ctx.info('reviews:handleEvent', { event, workspace: this.provider.getWorkspaceId() })
const { project, repository } = await this.provider.getProjectAndRepository(event.repository.node_id)
if (project === undefined || repository === undefined) {
this.ctx.info('No project for repository', {
ctx.info('No project for repository', {
name: event.repository.name,
workspace: this.provider.getWorkspaceId()
})
@@ -77,13 +84,14 @@ export class ReviewSyncManager implements DocSyncManager {
}
await this.eventSync.get(event.review.html_url)
const promise = this.processEvent(event, derivedClient, repository, integration)
const promise = this.processEvent(ctx, event, derivedClient, repository, integration)
this.eventSync.set(event.review.html_url, promise)
await promise
this.eventSync.delete(event.review.html_url)
}
async handleDelete (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
derivedClient: TxOperations,
@@ -105,7 +113,7 @@ export class ReviewSyncManager implements DocSyncManager {
if (commentExternal !== undefined) {
try {
await this.deleteGithubDocument(container, account, commentExternal.node_id)
await this.deleteGithubDocument(ctx, container, account, commentExternal.node_id)
} catch (err: any) {
let cnt = false
if (Array.isArray(err.errors)) {
@@ -118,7 +126,7 @@ export class ReviewSyncManager implements DocSyncManager {
}
}
if (!cnt) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
await derivedClient.update(info, { error: errorToObj(err) })
}
@@ -126,13 +134,18 @@ export class ReviewSyncManager implements DocSyncManager {
}
if (existing !== undefined && deleteExisting) {
await deleteObjects(this.ctx, this.client, [existing], account)
await deleteObjects(ctx, this.client, [existing], account)
}
return true
}
async deleteGithubDocument (container: ContainerFocus, account: PersonId, id: string): Promise<void> {
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
async deleteGithubDocument (
ctx: MeasureContext,
container: ContainerFocus,
account: PersonId,
id: string
): Promise<void> {
const okit = (await this.provider.getOctokit(ctx, account)) ?? container.container.octokit
const q = `mutation deleteReview($reviewID: ID!) {
deletePullRequestReview(input: {
pullRequestReviewId: $reviewID
@@ -150,6 +163,7 @@ export class ReviewSyncManager implements DocSyncManager {
}
private async processEvent (
ctx: MeasureContext,
event: PullRequestReviewEvent,
derivedClient: TxOperations,
repo: GithubIntegrationRepository,
@@ -175,7 +189,7 @@ export class ReviewSyncManager implements DocSyncManager {
)
externalData = response.node
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
return
}
@@ -266,7 +280,9 @@ export class ReviewSyncManager implements DocSyncManager {
}
}
@withContext('reviews-sync')
async sync (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
parent: DocSyncInfo | undefined,
@@ -288,7 +304,7 @@ export class ReviewSyncManager implements DocSyncManager {
}
// If no external document, we need to create it.
this.createCommentPromise = this.createGithubReview(container, existing, info, parent, derivedClient)
this.createCommentPromise = this.createGithubReview(ctx, container, existing, info, parent, derivedClient)
return await this.createCommentPromise
}
const review = info.external as ReviewExternalData
@@ -307,7 +323,7 @@ export class ReviewSyncManager implements DocSyncManager {
await syncChilds(info, this.client, derivedClient)
return { needSync: githubSyncVersion, current: messageData }
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
return { needSync: githubSyncVersion, error: errorToObj(err) }
}
@@ -388,6 +404,7 @@ export class ReviewSyncManager implements DocSyncManager {
}
async createGithubReview (
ctx: MeasureContext,
container: ContainerFocus,
existing: Doc | undefined,
info: DocSyncInfo,
@@ -405,7 +422,7 @@ export class ReviewSyncManager implements DocSyncManager {
return {}
}
const existingReview = existing as GithubReview
const okit = (await this.provider.getOctokit(existingReview.modifiedBy)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(ctx, existingReview.modifiedBy)) ?? container.container.octokit
// No external version yet, create it.
try {
@@ -452,13 +469,15 @@ export class ReviewSyncManager implements DocSyncManager {
}
return {}
} catch (err: any) {
this.ctx.error('Error', { err })
ctx.error('Error', { err })
Analytics.handleError(err)
return { needSync: githubSyncVersion, error: errorToObj(err) }
}
}
@withContext('reviews-externalSync')
async externalSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
kind: ExternalSyncField,
@@ -475,9 +494,11 @@ export class ReviewSyncManager implements DocSyncManager {
this.provider.sync()
}
repositoryDisabled (integration: IntegrationContainer, repo: GithubIntegrationRepository): void {}
repositoryDisabled (ctx: MeasureContext, integration: IntegrationContainer, repo: GithubIntegrationRepository): void {}
@withContext('reviews-externalFullSync')
async externalFullSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
projects: GithubProject[],
+11 -2
View File
@@ -22,6 +22,7 @@ export class UsersSyncManager implements DocSyncManager {
}
async sync (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
parent?: DocSyncInfo
@@ -30,6 +31,7 @@ export class UsersSyncManager implements DocSyncManager {
}
async handleDelete (
ctx: MeasureContext,
existing: Doc | undefined,
info: DocSyncInfo,
derivedClient: TxOperations,
@@ -38,9 +40,15 @@ export class UsersSyncManager implements DocSyncManager {
return false
}
async handleEvent<T>(integration: IntegrationContainer, derivedClient: TxOperations, evt: T): Promise<void> {}
async handleEvent<T>(
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
evt: T
): Promise<void> {}
async externalSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
kind: ExternalSyncField,
@@ -49,11 +57,12 @@ export class UsersSyncManager implements DocSyncManager {
prj: GithubProject
): Promise<void> {}
repositoryDisabled (integration: IntegrationContainer, repo: GithubIntegrationRepository): void {
repositoryDisabled (ctx: MeasureContext, integration: IntegrationContainer, repo: GithubIntegrationRepository): void {
integration.synchronized.delete(`${repo._id}:users`)
}
async externalFullSync (
ctx: MeasureContext,
integration: IntegrationContainer,
derivedClient: TxOperations,
projects: GithubProject[],