UBERF-8425: Global accounts (#7573)

This commit is contained in:
Alexey Zinoviev
2025-02-04 10:31:03 +07:00
committed by GitHub
parent 83e5b16392
commit 2c64ceadb8
765 changed files with 19134 additions and 17955 deletions
+10 -13
View File
@@ -2,9 +2,8 @@
// Copyright © 2023 Hardcore Engineering Inc.
//
import chunter, { ChatMessage } from '@hcengineering/chunter'
import { PersonAccount } from '@hcengineering/contact'
import core, {
Account,
PersonId,
AttachedData,
Doc,
DocumentUpdate,
@@ -61,7 +60,7 @@ export class CommentSyncManager implements DocSyncManager {
this.ctx.info('comments:handleEvent', {
action: event.action,
login: event.sender.login,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
if (event.sender.type === 'Bot') {
@@ -134,8 +133,8 @@ export class CommentSyncManager implements DocSyncManager {
return true
}
async deleteGithubDocument (container: ContainerFocus, account: Ref<Account>, id: string): Promise<void> {
const okit = (await this.provider.getOctokit(account as Ref<PersonAccount>)) ?? container.container.octokit
async deleteGithubDocument (container: ContainerFocus, account: PersonId, id: string): Promise<void> {
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
const q = `mutation deleteComment($commentID: ID!) {
deleteIssueComment(
@@ -160,7 +159,7 @@ export class CommentSyncManager implements DocSyncManager {
if (repo === undefined) {
this.ctx.info('No project for repository', {
repository: event.repository,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
return
}
@@ -305,7 +304,7 @@ export class CommentSyncManager implements DocSyncManager {
container: ContainerFocus,
parent: DocSyncInfo,
comment: CommentExternalData,
account: Ref<Account>
account: PersonId
): Promise<void> {
const repository = await this.provider.getRepositoryById(info.repository)
if (repository === undefined) {
@@ -337,8 +336,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 as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(existing.modifiedBy)) ?? container.container.octokit
await okit?.rest.issues.updateComment({
owner: repository.owner?.login as string,
repo: repository.name,
@@ -364,7 +362,7 @@ export class CommentSyncManager implements DocSyncManager {
messageData: MessageData,
parent: DocSyncInfo,
comment: CommentExternalData,
account: Ref<Account>
account: PersonId
): Promise<void> {
const _id: Ref<ChatMessage> = info._id as unknown as Ref<ChatMessage>
const value: AttachedData<ChatMessage> = {
@@ -407,8 +405,7 @@ export class CommentSyncManager implements DocSyncManager {
return {}
}
const chatMessage = existing as ChatMessage
const okit =
(await this.provider.getOctokit(chatMessage.modifiedBy as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(chatMessage.modifiedBy)) ?? container.container.octokit
// No external version yet, create it.
try {
@@ -508,7 +505,7 @@ export class CommentSyncManager implements DocSyncManager {
comments: comments.length,
used: data.headers['x-ratelimit-used'],
limit: data.headers['x-ratelimit-limit'],
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
await this.syncComments(repo, comments, derivedClient)
this.provider.sync()
+5 -5
View File
@@ -2,7 +2,7 @@
// Copyright © 2023 Hardcore Engineering Inc.
//
import { Branding, generateId, TxOperations, WorkspaceIdWithUrl } from '@hcengineering/core'
import { Branding, generateUuid, PersonUuid, TxOperations, WorkspaceIds, WorkspaceUuid } from '@hcengineering/core'
import { MarkupMarkType, MarkupNode, MarkupNodeType, traverseMarkupNode } from '@hcengineering/text'
import { getPublicLink } from '@hcengineering/server-guest-resources'
import { Task } from '@hcengineering/task'
@@ -49,13 +49,13 @@ export async function appendGuestLink (
client: TxOperations,
doc: Task,
markdown: MarkupNode,
workspace: WorkspaceIdWithUrl,
workspace: WorkspaceIds,
branding: Branding | null
): Promise<void> {
const publicLink = await getPublicLink(doc, client, workspace, false, branding)
await stripGuestLink(markdown)
appendGuestLinkToModel(markdown, publicLink, doc.identifier)
appendGuestLinkToImage(markdown, workspace)
appendGuestLinkToImage(markdown, workspace.uuid)
}
export function appendGuestLinkToModel (markdown: MarkupNode, publicLink: string, identifier: string): void {
@@ -89,14 +89,14 @@ function findImageTags (node: MarkupNode): MarkupNode[] {
return []
}
export function appendGuestLinkToImage (markdown: MarkupNode, workspace: WorkspaceIdWithUrl): void {
export function appendGuestLinkToImage (markdown: MarkupNode, workspace: WorkspaceUuid): void {
const imageTags: MarkupNode[] = markdown.content?.flatMap(findImageTags) ?? []
if (imageTags.length === 0) {
return
}
const id = generateId()
const id = generateUuid() as PersonUuid
const token = generateToken(id, workspace, { linkId: id, guest: 'true' })
for (const imageTag of imageTags) {
+339 -330
View File
@@ -6,12 +6,12 @@
TODO:
* Add since to synchronization
*/
/* eslint-disable @typescript-eslint/no-unused-vars */
import activity from '@hcengineering/activity'
import { Analytics } from '@hcengineering/analytics'
import { CollaboratorClient } from '@hcengineering/collaborator-client'
import { PersonAccount } from '@hcengineering/contact'
import core, {
Account,
PersonId,
AttachedDoc,
Class,
Doc,
@@ -138,17 +138,19 @@ export abstract class IssueSyncManagerBase {
this.provider = provider
}
async getAssignees (issue: IssueExternalData): Promise<PersonAccount[]> {
async getAssignees (issue: IssueExternalData): Promise<any[]> {
// TODO: FIXME
throw new Error('Not implemented')
// Find Assignees and reviewers
const assignees: PersonAccount[] = []
// const assignees: PersonAccount[] = []
for (const o of issue.assignees.nodes) {
const acc = await this.provider.getAccount(o)
if (acc !== undefined) {
assignees.push(acc)
}
}
return assignees
// for (const o of issue.assignees.nodes) {
// const acc = await this.provider.getAccount(o)
// if (acc !== undefined) {
// assignees.push(acc)
// }
// }
// return assignees
}
async processProjectV2Event (
@@ -227,7 +229,7 @@ export abstract class IssueSyncManagerBase {
return
}
this.ctx.info('event for issue', { url: syncData.url, workspace: this.provider.getWorkspaceId().name })
this.ctx.info('event for issue', { url: syncData.url, workspace: this.provider.getWorkspaceId() })
const externalData = syncData.external as IssueExternalData
// We need to replace field values we retrieved
target.prjData = externalData.projectItems.nodes.find(
@@ -255,7 +257,7 @@ export abstract class IssueSyncManagerBase {
})
if (event.changes.field_value === undefined) {
this.ctx.info('No changes for change event', { event, workspace: this.provider.getWorkspaceId().name })
this.ctx.info('No changes for change event', { event, workspace: this.provider.getWorkspaceId() })
return
}
@@ -338,7 +340,7 @@ export abstract class IssueSyncManagerBase {
external: IssueExternalData,
derivedClient: TxOperations,
update: IssueUpdate,
account: Ref<Account>,
account: PersonId,
prj: GithubProject,
needSync: boolean,
syncData?: DocSyncInfo,
@@ -639,7 +641,7 @@ export abstract class IssueSyncManagerBase {
this.ctx.error('error during field update', {
error: err,
response,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
errors.push({ error: err, response })
}
@@ -682,10 +684,10 @@ export abstract class IssueSyncManagerBase {
container: ContainerFocus,
issueExternal: IssueExternalData,
okit: Octokit,
account: Ref<Account>
account: PersonId
): Promise<boolean>
abstract afterSync (existing: Issue, account: Ref<Account>, issueExternal: any, info: DocSyncInfo): Promise<void>
abstract afterSync (existing: Issue, account: PersonId, issueExternal: any, info: DocSyncInfo): Promise<void>
async handleDiffUpdate (
target: IssueSyncTarget,
@@ -694,265 +696,267 @@ export abstract class IssueSyncManagerBase {
issueData: GithubIssueData,
container: ContainerFocus,
issueExternal: IssueExternalData,
account: Ref<Account>,
accountGH: Ref<Account>,
account: PersonId,
accountGH: PersonId,
syncToProject: boolean
): Promise<DocumentUpdate<DocSyncInfo>> {
let needUpdate = false
if (!this.client.getHierarchy().hasMixin(existing, github.mixin.GithubIssue)) {
await this.ctx.withLog(
'create mixin issue: GithubIssue',
{},
async () => {
await this.client.createMixin<Issue, GithubIssueP>(
existing._id as Ref<GithubIssueP>,
existing._class,
existing.space,
github.mixin.GithubIssue,
{
githubNumber: issueExternal.number,
url: issueExternal.url,
repository: info.repository as Ref<GithubIntegrationRepository>
}
)
await this.notifyConnected(container, info, existing, issueExternal)
},
{ identifier: existing.identifier, url: issueExternal.url }
)
// Re iterate to have existing value with mixin inside.
needUpdate = true
} else {
const ghIssue = this.client.getHierarchy().as(existing, github.mixin.GithubIssue)
await this.client.diffUpdate(ghIssue, {
githubNumber: issueExternal.number,
url: issueExternal.url,
repository: info.repository as Ref<GithubIntegrationRepository>
})
if (ghIssue.url !== issueExternal.url) {
await this.notifyConnected(container, info, existing, issueExternal)
}
}
if (!this.client.getHierarchy().hasMixin(existing, container.project.mixinClass)) {
await this.ctx.withLog(
'create mixin issue',
{},
() =>
this.client.createMixin<Issue, Issue>(
existing._id as Ref<GithubIssueP>,
existing._class,
existing.space,
container.project.mixinClass,
{}
),
{ identifier: existing.identifier, url: issueExternal.url }
)
// Re iterate to have existing value with mixin inside.
needUpdate = true
}
if (needUpdate) {
return { needSync: '' }
}
// TODO: FIXME
throw new Error('Not implemented')
// let needUpdate = false
// if (!this.client.getHierarchy().hasMixin(existing, github.mixin.GithubIssue)) {
// await this.ctx.withLog(
// 'create mixin issue: GithubIssue',
// {},
// async () => {
// await this.client.createMixin<Issue, GithubIssueP>(
// existing._id as Ref<GithubIssueP>,
// existing._class,
// existing.space,
// github.mixin.GithubIssue,
// {
// githubNumber: issueExternal.number,
// url: issueExternal.url,
// repository: info.repository as Ref<GithubIntegrationRepository>
// }
// )
// await this.notifyConnected(container, info, existing, issueExternal)
// },
// { identifier: existing.identifier, url: issueExternal.url }
// )
// // Re iterate to have existing value with mixin inside.
// needUpdate = true
// } else {
// const ghIssue = this.client.getHierarchy().as(existing, github.mixin.GithubIssue)
// await this.client.diffUpdate(ghIssue, {
// githubNumber: issueExternal.number,
// url: issueExternal.url,
// repository: info.repository as Ref<GithubIntegrationRepository>
// })
// if (ghIssue.url !== issueExternal.url) {
// await this.notifyConnected(container, info, existing, issueExternal)
// }
// }
// if (!this.client.getHierarchy().hasMixin(existing, container.project.mixinClass)) {
// await this.ctx.withLog(
// 'create mixin issue',
// {},
// () =>
// this.client.createMixin<Issue, Issue>(
// existing._id as Ref<GithubIssueP>,
// existing._class,
// existing.space,
// container.project.mixinClass,
// {}
// ),
// { identifier: existing.identifier, url: issueExternal.url }
// )
// // Re iterate to have existing value with mixin inside.
// needUpdate = true
// }
// if (needUpdate) {
// return { needSync: '' }
// }
const existingIssue = this.client.getHierarchy().as(existing, container.project.mixinClass)
const previousData: GithubIssueData = info.current ?? ({} as unknown as GithubIssueData)
const type = await this.provider.getTaskTypeOf(container.project.type, existing._class)
const stst = await this.provider.getStatuses(type?._id)
// const existingIssue = this.client.getHierarchy().as(existing, container.project.mixinClass)
// const previousData: GithubIssueData = info.current ?? ({} as unknown as GithubIssueData)
// const type = await this.provider.getTaskTypeOf(container.project.type, existing._class)
// const stst = await this.provider.getStatuses(type?._id)
const update = collectUpdate<Issue>(previousData, issueData, Object.keys(issueData))
// const update = collectUpdate<Issue>(previousData, issueData, Object.keys(issueData))
const allAttributes = this.client.getHierarchy().getAllAttributes(container.project.mixinClass)
const platformUpdate = collectUpdate<Issue>(previousData, existingIssue, Array.from(allAttributes.keys()))
// const allAttributes = this.client.getHierarchy().getAllAttributes(container.project.mixinClass)
// const platformUpdate = collectUpdate<Issue>(previousData, existingIssue, Array.from(allAttributes.keys()))
const okit = (await this.provider.getOctokit(account as Ref<PersonAccount>)) ?? container.container.octokit
// const okit = (await this.provider.getOctokit(account as PersonId)) ?? container.container.octokit
// Remove current same values from update
for (const [k, v] of Object.entries(update)) {
if ((existingIssue as any)[k] === v) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete (update as any)[k]
}
}
// // Remove current same values from update
// for (const [k, v] of Object.entries(update)) {
// if ((existingIssue as any)[k] === v) {
// // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
// delete (update as any)[k]
// }
// }
if (update.description !== undefined) {
if (update.description === existingIssue.description) {
delete update.description
}
}
// if (update.description !== undefined) {
// if (update.description === existingIssue.description) {
// delete update.description
// }
// }
for (const [k, v] of Object.entries(update)) {
let pv = (platformUpdate as any)[k]
// for (const [k, v] of Object.entries(update)) {
// let pv = (platformUpdate as any)[k]
if (k === 'description' && pv != null) {
const mdown = await this.provider.getMarkdown(pv)
pv = await this.provider.getMarkup(container.container, mdown, this.stripGuestLink)
}
if (pv != null && pv !== v) {
// We have conflict of values, assume platform is more proper one.
this.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]
continue
}
}
// if (k === 'description' && pv != null) {
// const mdown = await this.provider.getMarkdown(pv)
// pv = await this.provider.getMarkup(container.container, mdown, this.stripGuestLink)
// }
// if (pv != null && pv !== v) {
// // We have conflict of values, assume platform is more proper one.
// this.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]
// continue
// }
// }
await this.fillBackChanges(update, existingIssue, issueExternal)
// await this.fillBackChanges(update, existingIssue, issueExternal)
let needExternalSync = false
// let needExternalSync = false
if (container !== undefined && okit !== undefined) {
// Check and update issue fields.
needExternalSync = await this.performIssueFieldsUpdate(
info,
existing,
platformUpdate,
issueData,
container,
issueExternal,
okit,
account
)
// if (container !== undefined && okit !== undefined) {
// // Check and update issue fields.
// needExternalSync = await this.performIssueFieldsUpdate(
// info,
// existing,
// platformUpdate,
// issueData,
// container,
// issueExternal,
// okit,
// account
// )
const fieldsUpdate: { id: string, value: any, dataType: GithubDataType }[] = []
// const fieldsUpdate: { id: string, value: any, dataType: GithubDataType }[] = []
// Collect field update.
for (const [k, v] of Object.entries(platformUpdate)) {
const mapping = target.mappings.filter((it) => it != null).find((it) => it.name === k)
if (mapping === undefined) {
continue
}
const attr = this.client.getHierarchy().getAttribute(mapping._class, mapping.name)
// // Collect field update.
// for (const [k, v] of Object.entries(platformUpdate)) {
// const mapping = target.mappings.filter((it) => it != null).find((it) => it.name === k)
// if (mapping === undefined) {
// continue
// }
// const attr = this.client.getHierarchy().getAttribute(mapping._class, mapping.name)
if (attr.name === 'status') {
// Handle status field
const status = stst.find((it) => it._id === v) as Status
const optionId = this.findOptionId(container, mapping.githubId, status.name, target)
if (optionId !== undefined) {
fieldsUpdate.push({
id: mapping.githubId,
dataType: 'SINGLE_SELECT',
value: optionId
})
this.ctx.info(' => prepare issue status update', {
url: issueExternal.url,
name: status.name,
workspace: this.provider.getWorkspaceId().name
})
continue
}
}
if (attr.name === 'priority') {
const values: Record<IssuePriority, string> = {
[IssuePriority.NoPriority]: '',
[IssuePriority.High]: 'High',
[IssuePriority.Medium]: 'Medium',
[IssuePriority.Low]: 'Low',
[IssuePriority.Urgent]: 'Urgent'
}
// Handle priority field TODO: Add clear of field
const priorityName = values[v as IssuePriority]
const optionId = this.findOptionId(container, mapping.githubId, priorityName, target)
if (optionId !== undefined) {
fieldsUpdate.push({
id: mapping.githubId,
dataType: 'SINGLE_SELECT',
value: optionId
})
this.ctx.info(' => prepare issue priority update', {
url: issueExternal.url,
priority: priorityName,
workspace: this.provider.getWorkspaceId().name
})
continue
}
}
// if (attr.name === 'status') {
// // Handle status field
// const status = stst.find((it) => it._id === v) as Status
// const optionId = this.findOptionId(container, mapping.githubId, status.name, target)
// if (optionId !== undefined) {
// fieldsUpdate.push({
// id: mapping.githubId,
// dataType: 'SINGLE_SELECT',
// value: optionId
// })
// this.ctx.info(' => prepare issue status update', {
// url: issueExternal.url,
// name: status.name,
// workspace: this.provider.getWorkspaceId()
// })
// continue
// }
// }
// if (attr.name === 'priority') {
// const values: Record<IssuePriority, string> = {
// [IssuePriority.NoPriority]: '',
// [IssuePriority.High]: 'High',
// [IssuePriority.Medium]: 'Medium',
// [IssuePriority.Low]: 'Low',
// [IssuePriority.Urgent]: 'Urgent'
// }
// // Handle priority field TODO: Add clear of field
// const priorityName = values[v as IssuePriority]
// const optionId = this.findOptionId(container, mapping.githubId, priorityName, target)
// if (optionId !== undefined) {
// fieldsUpdate.push({
// id: mapping.githubId,
// dataType: 'SINGLE_SELECT',
// value: optionId
// })
// this.ctx.info(' => prepare issue priority update', {
// url: issueExternal.url,
// priority: priorityName,
// workspace: this.provider.getWorkspaceId()
// })
// continue
// }
// }
const dataType = getType(attr)
if (dataType === 'SINGLE_SELECT') {
// Handle status field
const optionId = this.findOptionId(container, mapping.githubId, v, target)
if (optionId !== undefined) {
fieldsUpdate.push({
id: mapping.githubId,
dataType: 'SINGLE_SELECT',
value: optionId
})
this.ctx.info(` => prepare issue field ${attr.label} update`, {
url: issueExternal.url,
value: v,
workspace: this.provider.getWorkspaceId().name
})
continue
}
}
// const dataType = getType(attr)
// if (dataType === 'SINGLE_SELECT') {
// // Handle status field
// const optionId = this.findOptionId(container, mapping.githubId, v, target)
// if (optionId !== undefined) {
// fieldsUpdate.push({
// id: mapping.githubId,
// dataType: 'SINGLE_SELECT',
// value: optionId
// })
// this.ctx.info(` => prepare issue field ${attr.label} update`, {
// url: issueExternal.url,
// value: v,
// workspace: this.provider.getWorkspaceId()
// })
// continue
// }
// }
if (dataType === undefined) {
continue
}
fieldsUpdate.push({
id: mapping.githubId,
dataType,
value: v
})
this.ctx.info(`=> prepare issue field ${attr.label} update`, {
url: issueExternal.url,
value: v,
workspace: this.provider.getWorkspaceId().name
})
}
if (fieldsUpdate.length > 0 && syncToProject && target.prjData !== undefined) {
const errors = await this.updateIssueValues(target, okit, fieldsUpdate)
if (errors.length === 0) {
needExternalSync = true
}
}
// TODO: Add support for labels, milestone, assignees
}
// if (dataType === undefined) {
// continue
// }
// fieldsUpdate.push({
// id: mapping.githubId,
// dataType,
// value: v
// })
// this.ctx.info(`=> prepare issue field ${attr.label} update`, {
// url: issueExternal.url,
// value: v,
// workspace: this.provider.getWorkspaceId()
// })
// }
// if (fieldsUpdate.length > 0 && syncToProject && target.prjData !== undefined) {
// const errors = await this.updateIssueValues(target, okit, fieldsUpdate)
// if (errors.length === 0) {
// needExternalSync = true
// }
// }
// // TODO: Add support for labels, milestone, assignees
// }
// We need remove all readonly field values
for (const k of Object.keys(update)) {
// Skip readonly fields
const attr = this.client.getHierarchy().findAttribute(target.project.mixinClass, k)
if (attr?.readonly === true) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete (update as any)[k]
continue
}
}
// // We need remove all readonly field values
// for (const k of Object.keys(update)) {
// // Skip readonly fields
// const attr = this.client.getHierarchy().findAttribute(target.project.mixinClass, k)
// if (attr?.readonly === true) {
// // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
// delete (update as any)[k]
// continue
// }
// }
// Update collaborative description
if (update.description !== undefined) {
this.ctx.info(`<= perform ${issueExternal.url} update to collaborator`, {
workspace: this.provider.getWorkspaceId().name
})
try {
const description = update.description as Markup
issueData.description = description
const collabId = makeDocCollabId(existingIssue, 'description')
await this.collaborator.updateMarkup(collabId, description)
} catch (err: any) {
Analytics.handleError(err)
this.ctx.error('error during description update', err)
}
}
// // Update collaborative description
// if (update.description !== undefined) {
// this.ctx.info(`<= perform ${issueExternal.url} update to collaborator`, {
// workspace: this.provider.getWorkspaceId()
// })
// try {
// const description = update.description as Markup
// issueData.description = description
// const collabId = makeDocCollabId(existingIssue, 'description')
// await this.collaborator.updateMarkup(collabId, description)
// } catch (err: any) {
// Analytics.handleError(err)
// this.ctx.error('error during description update', err)
// }
// }
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`, {
...update,
workspace: this.provider.getWorkspaceId().name
})
await this.client.update(existingIssue, update, false, new Date().getTime(), accountGH)
}
// 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`, {
// ...update,
// workspace: this.provider.getWorkspaceId()
// })
// await this.client.update(existingIssue, update, false, new Date().getTime(), accountGH)
// }
await this.afterSync(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,
needSync: githubSyncVersion,
...(needExternalSync ? { externalVersion: '' } : {}),
lastGithubUser: null
}
// await this.afterSync(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,
// needSync: githubSyncVersion,
// ...(needExternalSync ? { externalVersion: '' } : {}),
// lastGithubUser: null
// }
}
private async notifyConnected (
@@ -985,81 +989,83 @@ export abstract class IssueSyncManagerBase {
issueExternal: IssueExternalData,
_class: Ref<Class<Issue>>
): Promise<Record<string, any>> {
const issueUpdate: {
title?: string
body?: string
stateReason?: string
assigneeIds?: string[]
} & Record<string, any> = {}
if (platformUpdate.title != null) {
if (platformUpdate.title !== issueExternal.title) {
issueUpdate.title = platformUpdate.title
}
issueData.title = platformUpdate.title
}
if (platformUpdate.description != null) {
// Need to convert to markdown
issueUpdate.body = await this.provider.getMarkdown(platformUpdate.description ?? '')
issueData.description = await this.provider.getMarkup(
container.container,
issueUpdate.body ?? '',
this.stripGuestLink
)
// TODO: FIXME
throw new Error('Not implemented')
// const issueUpdate: {
// title?: string
// body?: string
// stateReason?: string
// assigneeIds?: string[]
// } & Record<string, any> = {}
// if (platformUpdate.title != null) {
// if (platformUpdate.title !== issueExternal.title) {
// issueUpdate.title = platformUpdate.title
// }
// issueData.title = platformUpdate.title
// }
// if (platformUpdate.description != null) {
// // Need to convert to markdown
// issueUpdate.body = await this.provider.getMarkdown(platformUpdate.description ?? '')
// issueData.description = await this.provider.getMarkup(
// container.container,
// issueUpdate.body ?? '',
// this.stripGuestLink
// )
// Of value is same, not need to update.
if (compareMarkdown(issueUpdate.body, issueExternal.body)) {
delete issueUpdate.body
}
}
if (platformUpdate.assignee !== undefined) {
const info =
platformUpdate.assignee !== null
? await this.provider.getGithubLogin(container.container, platformUpdate.assignee)
: undefined
// Check external
// // Of value is same, not need to update.
// if (compareMarkdown(issueUpdate.body, issueExternal.body)) {
// delete issueUpdate.body
// }
// }
// if (platformUpdate.assignee !== undefined) {
// const info =
// platformUpdate.assignee !== null
// ? await this.provider.getGithubLogin(container.container, platformUpdate.assignee)
// : undefined
// // Check external
const currentAssignees = issueExternal.assignees.nodes.map((it) => it.id)
currentAssignees.sort((a, b) => a.localeCompare(b))
// const currentAssignees = issueExternal.assignees.nodes.map((it) => it.id)
// currentAssignees.sort((a, b) => a.localeCompare(b))
issueUpdate.assigneeIds = info !== undefined ? [info.id] : []
issueUpdate.assigneeIds.sort((a, b) => a.localeCompare(b))
// issueUpdate.assigneeIds = info !== undefined ? [info.id] : []
// issueUpdate.assigneeIds.sort((a, b) => a.localeCompare(b))
if (deepEqual(currentAssignees, issueUpdate.assigneeIds)) {
// Same ids
delete issueUpdate.assigneeIds
}
issueData.assignee = platformUpdate.assignee
}
// if (deepEqual(currentAssignees, issueUpdate.assigneeIds)) {
// // Same ids
// delete issueUpdate.assigneeIds
// }
// issueData.assignee = platformUpdate.assignee
// }
const status = platformUpdate.status ?? issueData.status
const type = await this.provider.getTaskTypeOf(container.project.type, _class)
const statuses = await this.provider.getStatuses(type?._id)
const st = statuses.find((it) => it._id === status)
if (st !== undefined) {
// Need to convert to two operations.
switch (st.category) {
case task.statusCategory.UnStarted:
case task.statusCategory.ToDo:
case task.statusCategory.Active:
if (issueExternal.state !== 'OPEN') {
issueUpdate.state = 'OPEN'
}
break
case task.statusCategory.Won:
if (issueExternal.state !== 'CLOSED' || issueExternal.stateReason !== 'COMPLETED') {
issueUpdate.state = 'CLOSED'
issueUpdate.stateReason = 'COMPLETED'
}
break
case task.statusCategory.Lost:
if (issueExternal.state !== 'CLOSED' || issueExternal.stateReason !== 'NOT_PLANNED') {
issueUpdate.state = 'CLOSED'
issueUpdate.stateReason = 'not_planed' // Not supported change to github
}
break
}
}
return issueUpdate
// const status = platformUpdate.status ?? issueData.status
// const type = await this.provider.getTaskTypeOf(container.project.type, _class)
// const statuses = await this.provider.getStatuses(type?._id)
// const st = statuses.find((it) => it._id === status)
// if (st !== undefined) {
// // Need to convert to two operations.
// switch (st.category) {
// case task.statusCategory.UnStarted:
// case task.statusCategory.ToDo:
// case task.statusCategory.Active:
// if (issueExternal.state !== 'OPEN') {
// issueUpdate.state = 'OPEN'
// }
// break
// case task.statusCategory.Won:
// if (issueExternal.state !== 'CLOSED' || issueExternal.stateReason !== 'COMPLETED') {
// issueUpdate.state = 'CLOSED'
// issueUpdate.stateReason = 'COMPLETED'
// }
// break
// case task.statusCategory.Lost:
// if (issueExternal.state !== 'CLOSED' || issueExternal.stateReason !== 'NOT_PLANNED') {
// issueUpdate.state = 'CLOSED'
// issueUpdate.stateReason = 'not_planed' // Not supported change to github
// }
// break
// }
// }
// return issueUpdate
}
async syncIssues (
@@ -1086,14 +1092,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().name })
this.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().name })
this.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: '',
@@ -1112,7 +1118,7 @@ export abstract class IssueSyncManagerBase {
}
const externalEqual = deepEqual(existing.external, issue)
if (!externalEqual || existing.externalVersion !== githubExternalSyncVersion) {
this.ctx.info('Update sync doc', { url: issue.url, workspace: this.provider.getWorkspaceId().name })
this.ctx.info('Update sync doc', { url: issue.url, workspace: this.provider.getWorkspaceId() })
await ops.diffUpdate(
existing,
{
@@ -1178,7 +1184,7 @@ export abstract class IssueSyncManagerBase {
}
}
abstract deleteGithubDocument (container: ContainerFocus, account: Ref<Account>, id: string): Promise<void>
abstract deleteGithubDocument (container: ContainerFocus, account: PersonId, id: string): Promise<void>
async handleDelete (
existing: Doc | undefined,
@@ -1207,7 +1213,7 @@ export abstract class IssueSyncManagerBase {
}
const account =
existing?.createdBy ?? (await this.provider.getAccount(issueExternal.author))?._id ?? core.account.System
const okit = (await this.provider.getOctokit(account as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(account as PersonId)) ?? container.container.octokit
if (existing !== undefined && issueExternal !== undefined) {
let target = await this.getMilestoneIssueTarget(
@@ -1324,7 +1330,10 @@ export abstract class IssueSyncManagerBase {
const publicLink = await getPublicLink(
object,
this.client,
this.provider.getWorkspaceId(),
{
uuid: this.provider.getWorkspaceId(),
url: this.provider.getWorkspaceUrl()
},
false,
this.provider.getBranding()
)
+35 -36
View File
@@ -7,9 +7,8 @@
* Add since to synchronization
*/
import { Analytics } from '@hcengineering/analytics'
import { PersonAccount } from '@hcengineering/contact'
import core, {
Account,
PersonId,
AttachedData,
Doc,
DocumentUpdate,
@@ -53,17 +52,19 @@ import { getSince, gqlp, guessStatus, isGHWriteAllowed, syncRunner } from './uti
export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncManager {
createPromise: Promise<IssueExternalData | undefined> | undefined
externalDerivedSync = false
async getAssigneesI (issue: GithubIssue): Promise<PersonAccount[]> {
async getAssigneesI (issue: GithubIssue): Promise<any[]> {
// TODO: FIXME
throw new Error('Not implemented')
// Find Assignees and reviewers
const assignees: PersonAccount[] = []
// const assignees: PersonAccount[] = []
for (const o of issue.assignees) {
const acc = await this.provider.getAccountU(o)
if (acc !== undefined) {
assignees.push(acc)
}
}
return assignees
// for (const o of issue.assignees) {
// const acc = await this.provider.getAccountU(o)
// if (acc !== undefined) {
// assignees.push(acc)
// }
// }
// return assignees
}
async handleEvent<T = IssuesEvent | ProjectsV2ItemEvent>(
@@ -80,7 +81,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
login: event.sender.login,
type: event.sender.type,
url: event.sender.url,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
if (event.sender.type === 'Bot') {
@@ -112,7 +113,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
if (prj === undefined) {
this.ctx.info('Event from unknown v2 project', {
nodeId: projectV2Event.projects_v2_item.project_node_id,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
return
}
@@ -129,7 +130,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
this.ctx.info('No project for repository', {
repository: issueEvent.repository.name,
nodeId: issueEvent.repository.node_id,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
return
}
@@ -385,7 +386,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
this.ctx.info('create github issue', {
title: (existing as Issue).title,
number: (existing as Issue).number,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
const createdIssueData = await this.ctx.withLog(
'create github issue',
@@ -394,7 +395,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
this.createPromise = this.createGithubIssue(container, { ...(existing as Issue), description }, repository)
return await this.createPromise
},
{ id: (existing as Issue).identifier, workspace: this.provider.getWorkspaceId().name }
{ id: (existing as Issue).identifier, workspace: this.provider.getWorkspaceId() }
)
if (createdIssueData === undefined) {
this.ctx.error('Error create issue', { url: info.url })
@@ -491,7 +492,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
// A target node id
const targetNodeId: string | undefined = info.targetNodeId as string
const okit = (await this.provider.getOctokit(account as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(account as PersonId)) ?? container.container.octokit
const type = await this.provider.getTaskTypeOf(container.project.type, tracker.class.Issue)
const statuses = await this.provider.getStatuses(type?._id)
@@ -547,7 +548,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
try {
this.ctx.info('add issue to project v2', {
url: issueExternal.url,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
target.prjData = await this.ctx.withLog('add issue to project v2', {}, () =>
this.addIssueToProject(container, okit, issueExternal, target.target.projectNodeId as string)
@@ -571,7 +572,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
this.ctx.info('create platform issue', {
url: issueExternal.url,
title: issueExternal.title,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
const { markdownCompatible, markdown } = await this.provider.checkMarkdownConversion(
container.container,
@@ -664,7 +665,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
}
async afterSync (existing: Issue, update: DocumentUpdate<Doc>, account: Ref<Account>): Promise<void> {}
async afterSync (existing: Issue, update: DocumentUpdate<Doc>, account: PersonId): Promise<void> {}
async performIssueFieldsUpdate (
info: DocSyncInfo,
@@ -674,7 +675,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
container: ContainerFocus,
issueExternal: IssueExternalData,
okit: Octokit,
account: Ref<Account>
account: PersonId
): Promise<boolean> {
const { state, stateReason, body, ...issueUpdate } = await this.collectIssueUpdate(
info,
@@ -686,8 +687,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
tracker.class.Issue
)
const isLocked =
info.isDescriptionLocked === true && !(await this.provider.isPlatformUser(account as Ref<PersonAccount>))
const isLocked = info.isDescriptionLocked === true && !(await this.provider.isPlatformUser(account))
const hasFieldStateChanges = Object.keys(issueUpdate).length > 0 || state !== undefined
// We should allow modification from user.
@@ -737,7 +737,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
url: issueExternal.url,
...issueUpdate,
body,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
if (isGHWriteAllowed()) {
if (state === 'OPEN') {
@@ -773,7 +773,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
'==> updateIssue',
{},
async () => {
this.ctx.info('update fields', { ...issueUpdate, workspace: this.provider.getWorkspaceId().name })
this.ctx.info('update fields', { ...issueUpdate, workspace: this.provider.getWorkspaceId() })
if (isGHWriteAllowed()) {
const hasOtherChanges = Object.keys(issueUpdate).length > 0
if (state === 'OPEN') {
@@ -817,8 +817,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
): Promise<IssueExternalData | undefined> {
const existingIssue = existing
const okit =
(await this.provider.getOctokit(existingIssue.modifiedBy as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(existingIssue.modifiedBy)) ?? container.container.octokit
const repoId = repository.nodeId
@@ -858,8 +857,8 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
}
}
async deleteGithubDocument (container: ContainerFocus, account: Ref<Account>, id: string): Promise<void> {
const okit = (await this.provider.getOctokit(account as Ref<PersonAccount>)) ?? container.container.octokit
async deleteGithubDocument (container: ContainerFocus, account: PersonId, id: string): Promise<void> {
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
const q = `mutation deleteIssue($issueID: ID!) {
deleteIssue(
@@ -879,7 +878,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
private async createNewIssue (
info: DocSyncInfo,
account: Ref<Account>,
account: PersonId,
issueData: GithubIssueData & { status: Issue['status'] },
issueExternal: IssueExternalData,
repo: Ref<GithubIntegrationRepository>,
@@ -1020,7 +1019,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
if (issues.some((issue) => issue.url === undefined && Object.keys(issue).length === 0)) {
this.ctx.error('empty document content', {
repo: repo.name,
workspace: this.provider.getWorkspaceId().name,
workspace: this.provider.getWorkspaceId(),
data: cutObjectArray(response)
})
}
@@ -1033,7 +1032,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
this.ctx.warn('issue external retrieval switch to one by one mode', {
errors: err.errors,
msg: err.message,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
} else if (partsize === 1) {
// We need to update issue, since it is missing on external side.
@@ -1043,7 +1042,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
errors: err.errors,
msg: err.message,
url: syncDoc.url,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
await derivedClient.diffUpdate(
syncDoc,
@@ -1109,7 +1108,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().name })
this.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) {
@@ -1143,7 +1142,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
if (issues.some((issue) => issue.url === undefined && Object.keys(issue).length === 0)) {
this.ctx.error('empty document content', {
repo: repo.name,
workspace: this.provider.getWorkspaceId().name,
workspace: this.provider.getWorkspaceId(),
data: cutObjectArray(data)
})
}
@@ -1158,7 +1157,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
this.ctx.info('sync external issues - done', {
repo: repo.name,
since,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
integration.synchronized.add(syncKey)
}
@@ -1,5 +1,4 @@
import { Analytics } from '@hcengineering/analytics'
import { PersonAccount } from '@hcengineering/contact'
import core, {
AnyAttribute,
Class,
@@ -107,10 +106,14 @@ export class ProjectsSyncManager implements DocSyncManager {
return { needSync: githubSyncVersion }
}
const okit = await this.provider.getOctokit(container.project.createdBy as Ref<PersonAccount>)
if (container.project.createdBy === undefined) {
return
}
const okit = await this.provider.getOctokit(container.project.createdBy)
if (okit === undefined) {
this.ctx.info('No Authentication for author, waiting for authentication.', {
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
return { needSync: githubSyncVersion, error: 'Need authentication for user' }
}
@@ -270,8 +273,7 @@ export class ProjectsSyncManager implements DocSyncManager {
const allAttributes = this.client.getHierarchy().getAllAttributes(tracker.class.Milestone)
const platformUpdate = collectUpdate<Milestone>(previousData, existingMilestone, Array.from(allAttributes.keys()))
const okit =
(await this.provider.getOctokit(existing.modifiedBy as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(existing.modifiedBy)) ?? container.container.octokit
// Remove current same values from update
for (const [k, v] of Object.entries(update)) {
@@ -322,7 +324,7 @@ export class ProjectsSyncManager implements DocSyncManager {
if (project === undefined || repository === undefined) {
this.ctx.error('Unable to find project and repository for event', {
name: event.repository.name,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
return
}
@@ -392,10 +394,10 @@ export class ProjectsSyncManager implements DocSyncManager {
continue
}
const okit = await this.provider.getOctokit(integration.integration.createdBy as Ref<PersonAccount>)
const okit = await this.provider.getOctokit(integration.integration.createdBy)
if (okit === undefined) {
this.ctx.info('No Authentication for author, waiting for authentication.', {
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
continue
}
@@ -1,7 +1,8 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Analytics } from '@hcengineering/analytics'
import { Person, PersonAccount } from '@hcengineering/contact'
import { Person } from '@hcengineering/contact'
import core, {
Account,
PersonId,
AttachedData,
Doc,
DocumentUpdate,
@@ -93,7 +94,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
action: _event.action,
login: _event.sender.login,
type: _event.sender.type,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
const projectV2Event = (_event as any as ProjectsV2ItemEvent).projects_v2_item?.id !== undefined
@@ -117,7 +118,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
if (prj === undefined) {
this.ctx.info('Event from unknown v2 project', {
nodeId: projectV2Event.projects_v2_item.project_node_id,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
return
}
@@ -134,7 +135,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
if (project === undefined || repository === undefined) {
this.ctx.info('No project for repository', {
name: event.repository.name,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
return
}
@@ -321,33 +322,33 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
}
async getReviewers (issue: PullRequestExternalData): Promise<PersonAccount[]> {
// Find Assignees and reviewers
const ids: UserInfo[] = issue.reviewRequests.nodes.map((it: any) => it.requestedReviewer)
// async getReviewers (issue: PullRequestExternalData): Promise<PersonAccount[]> {
// // Find Assignees and reviewers
// const ids: UserInfo[] = issue.reviewRequests.nodes.map((it: any) => it.requestedReviewer)
const values: PersonAccount[] = []
// const values: PersonAccount[] = []
for (const o of ids) {
const acc = await this.provider.getAccount(o)
if (acc !== undefined) {
values.push(acc)
}
}
// for (const o of ids) {
// const acc = await this.provider.getAccount(o)
// if (acc !== undefined) {
// values.push(acc)
// }
// }
for (const n of issue.latestReviews.nodes) {
const acc = await this.provider.getAccount(n.author)
if (acc !== undefined) {
values.push(acc)
}
}
return values
}
// for (const n of issue.latestReviews.nodes) {
// const acc = await this.provider.getAccount(n.author)
// if (acc !== undefined) {
// values.push(acc)
// }
// }
// return values
// }
private async createSyncData (
pullRequestExternal: PullRequestExternalData,
derivedClient: TxOperations,
repo: GithubIntegrationRepository,
account: Ref<Account>
account: PersonId
): Promise<void> {
const lastModified = new Date(pullRequestExternal.updatedAt).getTime()
await derivedClient.createDoc(github.class.DocSyncInfo, repo.githubProject as Ref<GithubProject>, {
@@ -389,7 +390,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
// A target node id
const targetNodeId: string | undefined = info.targetNodeId as string
const okit = (await this.provider.getOctokit(account as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(account as PersonId)) ?? container.container.octokit
const isProjectProjectTarget = target.target.projectNodeId === target.project.projectNodeId
const supportProjects =
@@ -447,7 +448,8 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
const assignees = await this.getAssignees(pullRequestExternal)
const reviewers = await this.getReviewers(pullRequestExternal)
// TODO: FIXME
const reviewers: any = [] // await this.getReviewers(pullRequestExternal)
const latestReviews: LastReviewState[] = []
@@ -464,7 +466,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
title: pullRequestExternal.title,
description: await this.provider.getMarkup(container.container, pullRequestExternal.body, this.stripGuestLink),
assignee: assignees[0]?.person ?? null,
reviewers: reviewers.map((it) => it.person),
reviewers: reviewers.map((it: any) => it.person),
draft: pullRequestExternal.isDraft,
head: pullRequestExternal.headRef,
base: pullRequestExternal.baseRef,
@@ -634,7 +636,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
}
async afterSync (existing: Issue, account: Ref<Account>, issueExternal: any, info: DocSyncInfo): Promise<void> {
async afterSync (existing: Issue, account: PersonId, issueExternal: any, info: DocSyncInfo): Promise<void> {
const pullRequest = existing as GithubPullRequest
await this.todoSync(this.client, pullRequest, issueExternal as PullRequestExternalData, info, account)
}
@@ -647,7 +649,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
>,
external: PullRequestExternalData,
info: DocSyncInfo,
account: Ref<Account>
account: PersonId
): Promise<void> {
// Find all todo's related to PR.
const allTodos = await client.findAll<GithubTodo>(github.mixin.GithubTodo, { attachedTo: pullRequest._id })
@@ -826,7 +828,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
pullRequest: Pick<GithubPullRequest, '_id' | 'identifier' | 'space' | '_class' | 'reviewers' | 'title' | 'state'>,
external: PullRequestExternalData,
todoUser: Ref<Person>,
account: Ref<Account>
account: PersonId
): Promise<void> {
const latestTodo = await client.findOne(
time.class.ToDo,
@@ -879,7 +881,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
>,
external: PullRequestExternalData,
todoUser: Ref<Person>,
account: Ref<Account>
account: PersonId
): Promise<void> {
const latestTodo = await client.findOne(
time.class.ToDo,
@@ -1011,7 +1013,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
container: ContainerFocus,
issueExternal: IssueExternalData,
okit: Octokit,
account: Ref<Account>
account: PersonId
): Promise<boolean> {
let { state, stateReason, body, ...issueUpdate } = await this.collectIssueUpdate(
info,
@@ -1029,8 +1031,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
const hasFieldsUpdate = Object.keys(issueUpdate).length > 0 || state !== undefined
const isLocked =
info.isDescriptionLocked === true && !(await this.provider.isPlatformUser(account as Ref<PersonAccount>))
const isLocked = info.isDescriptionLocked === true && !(await this.provider.isPlatformUser(account))
if (hasFieldsUpdate || body !== undefined) {
if (body !== undefined && !isLocked) {
@@ -1042,7 +1043,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
url: issueExternal.url,
...issueUpdate,
body,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
if (isGHWriteAllowed()) {
await okit?.graphql(
@@ -1072,7 +1073,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
this.ctx.info('update-fields', {
url: issueExternal.url,
...issueUpdate,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
if (isGHWriteAllowed()) {
await okit?.graphql(
@@ -1105,7 +1106,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
pullRequestExternal: PullRequestExternalData,
existingPR: Pick<GithubPullRequest, '_id' | 'space' | '_class'>,
lastModified: number,
account: Ref<Account>
account: PersonId
): Promise<void> {
const repo = await this.provider.getRepositoryById(info.repository)
if (repo?.nodeId === undefined) {
@@ -1156,7 +1157,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
private async createPullRequest (
client: TxOperations,
info: DocSyncInfo,
account: Ref<Account>,
account: PersonId,
pullRequestData: GithubPullRequestData & { status: Issue['status'] },
pullRequestExternal: PullRequestExternalData,
repo: Ref<GithubIntegrationRepository>,
@@ -1375,7 +1376,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
if (issues.some((issue) => issue.url === undefined && Object.keys(issue).length === 0)) {
this.ctx.error('empty document content updates', {
repo: repo.name,
workspace: this.provider.getWorkspaceId().name,
workspace: this.provider.getWorkspaceId(),
data: cutObjectArray(response)
})
}
@@ -1387,7 +1388,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
this.ctx.warn('pull request external retrieval switch to one by one mode', {
errors: err.errors,
msg: err.message,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
} else if (partsize === 1) {
// We need to update issue, since it is missing on external side.
@@ -1397,7 +1398,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
errors: err.errors,
msg: err.message,
url: syncDoc.url,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
await derivedClient.diffUpdate(
syncDoc,
@@ -1467,7 +1468,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
this.ctx.info('sync external pull requests', {
repo: repo.name,
since,
workspace: this.provider.getWorkspaceId().name,
workspace: this.provider.getWorkspaceId(),
state: 'OPEN'
})
await this.performPRSync(integration, repo, 'OPEN', undefined, derivedClient, prj)
@@ -1475,7 +1476,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
this.ctx.info('sync external pull requests', {
repo: repo.name,
since,
workspace: this.provider.getWorkspaceId().name,
workspace: this.provider.getWorkspaceId(),
state: 'CLOSED, MERGED'
})
await this.performPRSync(integration, repo, 'CLOSED, MERGED', since, derivedClient, prj)
@@ -1483,7 +1484,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
this.ctx.info('sync external pull requests - done', {
repo: repo.name,
since,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
this.provider.sync()
@@ -1535,7 +1536,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
repo: repo.name,
since,
len: issues.length,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
if (since !== undefined) {
@@ -1552,7 +1553,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
if (emptyIndex !== -1) {
this.ctx.error('empty document content', {
repo: repo.name,
workspace: this.provider.getWorkspaceId().name,
workspace: this.provider.getWorkspaceId(),
data: cutObjectArray(data),
emptyIndex,
el: JSON.stringify(issues[emptyIndex])
@@ -1593,7 +1594,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
return { patch, contentType }
}
async deleteGithubDocument (container: ContainerFocus, account: Ref<Account>, id: string): Promise<void> {
async deleteGithubDocument (container: ContainerFocus, account: PersonId, id: string): Promise<void> {
// No delete is allowed for pull requests
}
}
@@ -95,7 +95,7 @@ export class RepositorySyncMapper implements DocSyncManager {
)
this.ctx.info('Creating repository info document...', {
url: repository.full_name,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
}
}
@@ -126,7 +126,7 @@ export class RepositorySyncMapper implements DocSyncManager {
)
this.ctx.info('Creating repository info document...', {
url: event.repository.url,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
break
}
@@ -244,7 +244,7 @@ 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().name })
this.ctx.info('no installation found', { workspace: this.provider.getWorkspaceId() })
return
}
@@ -253,7 +253,7 @@ export class RepositorySyncMapper implements DocSyncManager {
}
this.ctx.info('Checking github installation repositories...', {
installationId: integration.installationId,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
const iterable = this.app.eachRepository.iterator({ installationId: integration.installationId })
@@ -299,7 +299,7 @@ export class RepositorySyncMapper implements DocSyncManager {
)
this.ctx.info('Creating repository info document...', {
url: repository.url,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
} else {
allRepos = allRepos.filter((it) => it._id !== integrationRepo._id)
@@ -315,7 +315,7 @@ export class RepositorySyncMapper implements DocSyncManager {
this.ctx.info('processing repository diff update...', {
repository: repository.name,
...diff,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
await this.client.diffUpdate(
integrationRepo,
@@ -360,7 +360,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().name })
this.ctx.info('handle repository rename', { repo, workspace: this.provider.getWorkspaceId() })
const update = async (): Promise<void> => {
while (true) {
const docs = await this.client.findAll(
@@ -1,9 +1,8 @@
//
// Copyright © 2023 Hardcore Engineering Inc.
//
import { PersonAccount } from '@hcengineering/contact'
import core, {
Account,
PersonId,
AttachedData,
Doc,
DocData,
@@ -70,13 +69,13 @@ export class ReviewCommentSyncManager implements DocSyncManager {
this.ctx.info('reviewComments:handleEvent', {
action: event.action,
login: event.sender.login,
workspace: this.provider.getWorkspaceId().name
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', {
name: event.repository.name,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
return
}
@@ -146,12 +145,12 @@ export class ReviewCommentSyncManager implements DocSyncManager {
async deleteGithubDocument (
container: ContainerFocus,
account: Ref<Account>,
account: PersonId,
id: string,
derivedClient: TxOperations,
parent?: DocSyncInfo
): Promise<void> {
const okit = (await this.provider.getOctokit(account as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
const q = `mutation deleteReviewComment($reviewID: ID!) {
deletePullRequestReviewComment(input: {
id: $reviewID
@@ -382,7 +381,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
container: ContainerFocus,
parent: DocSyncInfo,
reviewComment: ReviewCommentExternalData,
account: Ref<Account>,
account: PersonId,
derivedClient: TxOperations
): Promise<void> {
const repository = await this.provider.getRepositoryById(info.repository)
@@ -416,7 +415,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
if (Object.keys(platformUpdate).length > 0) {
if (platformUpdate.body !== undefined) {
const body = await this.provider.getMarkup(container.container, platformUpdate.body)
const okit = (await this.provider.getOctokit(account as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
const q = `mutation updateReviewComment($commentID: ID!, $body: String!) {
updatePullRequestReviewComment(input: {
threadId: $threadID
@@ -450,7 +449,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
messageData: ReviewCommentData,
parent: DocSyncInfo,
review: ReviewCommentExternalData,
account: Ref<Account>
account: PersonId
): Promise<void> {
const _id: Ref<GithubReviewComment> = info._id as unknown as Ref<GithubReviewComment>
const value: AttachedData<GithubReviewComment> = {
@@ -487,8 +486,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
return {}
}
const existingReview = existing as GithubReviewComment
const okit =
(await this.provider.getOctokit(existingReview.modifiedBy as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(existingReview.modifiedBy)) ?? container.container.octokit
// No external version yet, create it.
try {
@@ -1,9 +1,8 @@
//
// Copyright © 2023 Hardcore Engineering Inc.
//
import { PersonAccount } from '@hcengineering/contact'
import core, {
Account,
PersonId,
AttachedData,
Doc,
DocumentUpdate,
@@ -89,13 +88,13 @@ export class ReviewThreadSyncManager implements DocSyncManager {
return
}
}
this.ctx.info('reviewThreads:handleEvent', { event, workspace: this.provider.getWorkspaceId().name })
this.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', {
name: event.repository.name,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
return
}
@@ -163,7 +162,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
return true
}
async deleteGithubDocument (container: ContainerFocus, account: Ref<Account>, id: string): Promise<void> {
async deleteGithubDocument (container: ContainerFocus, account: PersonId, id: string): Promise<void> {
// Not supported
}
@@ -330,7 +329,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
container: ContainerFocus,
parent: DocSyncInfo,
review: ReviewThreadExternalData,
account: Ref<Account>,
account: PersonId,
derivedClient: TxOperations
): Promise<void> {
const repository = await this.provider.getRepositoryById(info.repository)
@@ -364,7 +363,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 as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
const q = `mutation updateReviewThread($threadID: ID!) {
${platformUpdate.isResolved ? 'resolveReviewThread' : 'unresolveReviewThread'} (
input: {
@@ -401,7 +400,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
messageData: ReviewThreadData,
parent: DocSyncInfo,
review: ReviewThreadExternalData,
account: Ref<Account>
account: PersonId
): Promise<void> {
const _id: Ref<GithubReviewThread> = info._id as unknown as Ref<GithubReviewThread>
const value: AttachedData<GithubReviewThread> = {
@@ -438,8 +437,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
return {}
}
const existingReview = existing as GithubReviewThread
const okit =
(await this.provider.getOctokit(existingReview.modifiedBy as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(existingReview.modifiedBy)) ?? container.container.octokit
// No external version yet, create it.
// Will be added into pending state.
+8 -10
View File
@@ -1,9 +1,8 @@
//
// Copyright © 2023 Hardcore Engineering Inc.
//
import { PersonAccount } from '@hcengineering/contact'
import core, {
Account,
PersonId,
AttachedData,
Doc,
DocumentUpdate,
@@ -67,13 +66,13 @@ export class ReviewSyncManager implements DocSyncManager {
return
}
}
this.ctx.info('reviews:handleEvent', { event, workspace: this.provider.getWorkspaceId().name })
this.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', {
name: event.repository.name,
workspace: this.provider.getWorkspaceId().name
workspace: this.provider.getWorkspaceId()
})
return
}
@@ -141,8 +140,8 @@ export class ReviewSyncManager implements DocSyncManager {
return true
}
async deleteGithubDocument (container: ContainerFocus, account: Ref<Account>, id: string): Promise<void> {
const okit = (await this.provider.getOctokit(account as Ref<PersonAccount>)) ?? container.container.octokit
async deleteGithubDocument (container: ContainerFocus, account: PersonId, id: string): Promise<void> {
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
const q = `mutation deleteReview($reviewID: ID!) {
deletePullRequestReview(input: {
pullRequestReviewId: $reviewID
@@ -334,7 +333,7 @@ export class ReviewSyncManager implements DocSyncManager {
container: ContainerFocus,
parent: DocSyncInfo,
review: ReviewExternalData,
account: Ref<Account>
account: PersonId
): Promise<void> {
const repository = await this.provider.getRepositoryById(info.repository)
if (repository === undefined) {
@@ -378,7 +377,7 @@ export class ReviewSyncManager implements DocSyncManager {
messageData: ReviewData,
parent: DocSyncInfo,
review: ReviewExternalData,
account: Ref<Account>
account: PersonId
): Promise<void> {
const _id: Ref<GithubReview> = info._id as unknown as Ref<GithubReview>
const value: AttachedData<GithubReview> = {
@@ -415,8 +414,7 @@ export class ReviewSyncManager implements DocSyncManager {
return {}
}
const existingReview = existing as GithubReview
const okit =
(await this.provider.getOctokit(existingReview.modifiedBy as Ref<PersonAccount>)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(existingReview.modifiedBy)) ?? container.container.octokit
// No external version yet, create it.
try {
+2 -2
View File
@@ -1,6 +1,6 @@
import { Analytics } from '@hcengineering/analytics'
import core, {
Account,
PersonId,
AnyAttribute,
AttachedDoc,
Class,
@@ -297,7 +297,7 @@ export async function deleteObjects (
ctx: MeasureContext,
client: TxOperations,
objects: Doc[],
account: Ref<Account>
account: PersonId
): Promise<void> {
const ops = client.apply()
for (const object of objects) {