UBERF-9724: Use updated accounts (#8452)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev
2025-04-14 09:36:20 +07:00
committed by GitHub
parent 143f0bc7b6
commit 5ca2a73fba
28 changed files with 1506 additions and 1153 deletions
@@ -104,7 +104,7 @@ export class CommentSyncManager implements DocSyncManager {
return true
}
const account =
existing?.createdBy ?? (await this.provider.getAccountU(commentExternal.user))?._id ?? core.account.System
existing?.createdBy ?? (await this.provider.getAccountU(commentExternal.user)) ?? core.account.System
if (commentExternal !== undefined) {
try {
@@ -164,7 +164,7 @@ export class CommentSyncManager implements DocSyncManager {
return
}
const account = (await this.provider.getAccountU(event.sender))?._id ?? core.account.System
const account = (await this.provider.getAccountU(event.sender)) ?? core.account.System
switch (event.action) {
case 'created': {
await this.createSyncData(event, derivedClient, repo)
@@ -277,7 +277,7 @@ export class CommentSyncManager implements DocSyncManager {
return { needSync: githubSyncVersion }
}
const account = existing?.modifiedBy ?? (await this.provider.getAccountU(comment.user))?._id ?? core.account.System
const account = existing?.modifiedBy ?? (await this.provider.getAccountU(comment.user)) ?? core.account.System
const messageData: MessageData = {
message: await this.provider.getMarkupSafe(container.container, comment.body)
+324 -326
View File
@@ -10,14 +10,15 @@
import activity from '@hcengineering/activity'
import { Analytics } from '@hcengineering/analytics'
import { CollaboratorClient } from '@hcengineering/collaborator-client'
import contact, { Person } from '@hcengineering/contact'
import core, {
PersonId,
AttachedDoc,
Class,
Doc,
DocumentUpdate,
Markup,
MeasureContext,
PersonId,
Ref,
Space,
Status,
@@ -29,7 +30,6 @@ import github, {
GithubFieldMapping,
GithubIntegrationRepository,
GithubIssue,
GithubIssue as GithubIssueP,
GithubMilestone,
GithubProject
} from '@hcengineering/github'
@@ -138,19 +138,22 @@ export abstract class IssueSyncManagerBase {
this.provider = provider
}
async getAssignees (issue: IssueExternalData): Promise<any[]> {
// TODO: FIXME
throw new Error('Not implemented')
async getAssignees (issue: IssueExternalData): Promise<Ref<Person>[]> {
// Find Assignees and reviewers
// const assignees: PersonAccount[] = []
const assignees: PersonId[] = []
// 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 await this.getPersonsFromId(assignees)
}
async getPersonsFromId (assignees: PersonId[]): Promise<Ref<Person>[]> {
const socialIds = await this.client.findAll(contact.class.SocialIdentity, { _id: { $in: assignees as any } })
return socialIds.map((it) => it.attachedTo)
}
async processProjectV2Event (
@@ -159,7 +162,7 @@ export abstract class IssueSyncManagerBase {
derivedClient: TxOperations,
prj: GithubProject
): Promise<void> {
const account = (await this.provider.getAccountU(event.sender))?._id ?? core.account.System
const account = (await this.provider.getAccountU(event.sender)) ?? core.account.System
switch (event.action) {
case 'edited': {
const itemId = event.projects_v2_item.node_id
@@ -708,263 +711,261 @@ export abstract class IssueSyncManagerBase {
accountGH: PersonId,
syncToProject: boolean
): Promise<DocumentUpdate<DocSyncInfo>> {
// 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: '' }
// }
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, GithubIssue>(
existing._id as Ref<GithubIssue>,
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<GithubIssue>,
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 PersonId)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(account)) ?? 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.getMarkupSafe(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.getMarkupSafe(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()
// })
// 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
// }
// }
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()
// })
// 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()
// })
// }
// 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()
// })
// 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()
// })
// 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 (
@@ -997,83 +998,81 @@ export abstract class IssueSyncManagerBase {
issueExternal: IssueExternalData,
_class: Ref<Class<Issue>>
): Promise<Record<string, any>> {
// 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.getMarkupSafe(
// container.container,
// issueUpdate.body ?? '',
// this.stripGuestLink
// )
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.getMarkupSafe(
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 (
@@ -1219,9 +1218,8 @@ export abstract class IssueSyncManagerBase {
// No external issue yet, safe delete, since platform document will be deleted a well.
return true
}
const account =
existing?.createdBy ?? (await this.provider.getAccount(issueExternal.author))?._id ?? core.account.System
const okit = (await this.provider.getOctokit(account as PersonId)) ?? container.container.octokit
const account = existing?.createdBy ?? (await this.provider.getAccount(issueExternal.author)) ?? core.account.System
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
if (existing !== undefined && issueExternal !== undefined) {
let target = await this.getMilestoneIssueTarget(
+16 -17
View File
@@ -52,19 +52,17 @@ 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<any[]> {
// TODO: FIXME
throw new Error('Not implemented')
async getAssigneesI (issue: GithubIssue): Promise<PersonId[]> {
// Find Assignees and reviewers
// const assignees: PersonAccount[] = []
const assignees: PersonId[] = []
// 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>(
@@ -150,7 +148,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
integration: IntegrationContainer,
prj: GithubProject
): Promise<void> {
const account = (await this.provider.getAccountU(event.sender))?._id ?? core.account.System
const account = (await this.provider.getAccountU(event.sender)) ?? core.account.System
let externalData: IssueExternalData | undefined
if (event.action !== 'deleted') {
@@ -243,8 +241,9 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
case 'assigned':
case 'unassigned': {
const assignees = await this.getAssigneesI(event.issue)
const persons = await this.getPersonsFromId(assignees)
const update: IssueUpdate = {
assignee: assignees?.[0]?.person ?? null
assignee: persons?.[0] ?? null
}
await this.handleUpdate(externalData as IssueExternalData, derivedClient, update, account, prj, false)
break
@@ -481,9 +480,9 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
info: DocSyncInfo
): Promise<DocumentUpdate<DocSyncInfo>> {
const account =
existing?.modifiedBy ?? (await this.provider.getAccount(issueExternal.author))?._id ?? core.account.System
existing?.modifiedBy ?? (await this.provider.getAccount(issueExternal.author)) ?? core.account.System
const accountGH =
info.lastGithubUser ?? (await this.provider.getAccount(issueExternal.author))?._id ?? core.account.System
info.lastGithubUser ?? (await this.provider.getAccount(issueExternal.author)) ?? core.account.System
const isProjectProjectTarget = target.target.projectNodeId === target.project.projectNodeId
const supportProjects =
@@ -492,7 +491,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 PersonId)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
const type = await this.provider.getTaskTypeOf(container.project.type, tracker.class.Issue)
const statuses = await this.provider.getStatuses(type?._id)
@@ -502,7 +501,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
const issueData = {
title: issueExternal.title,
description: await this.provider.getMarkupSafe(container.container, issueExternal.body, this.stripGuestLink),
assignee: assignees[0]?.person,
assignee: assignees[0],
repository: info.repository,
remainingTime: 0
}
@@ -2,10 +2,10 @@
import { Analytics } from '@hcengineering/analytics'
import { Person } from '@hcengineering/contact'
import core, {
PersonId,
AttachedData,
Doc,
DocumentUpdate,
PersonId,
Ref,
SortingOrder,
Status,
@@ -41,10 +41,10 @@ import {
DocSyncManager,
ExternalSyncField,
IntegrationContainer,
UserInfo,
githubDerivedSyncVersion,
githubExternalSyncVersion,
githubSyncVersion
githubSyncVersion,
type UserInfo
} from '../types'
import {
IssueExternalData,
@@ -154,7 +154,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
integration: IntegrationContainer,
prj: GithubProject
): Promise<void> {
const account = (await this.provider.getAccountU(event.sender))?._id ?? core.account.System
const account = (await this.provider.getAccountU(event.sender)) ?? core.account.System
let externalData: PullRequestExternalData
try {
@@ -243,7 +243,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
case 'unassigned': {
const assignees = await this.getAssignees(externalData)
const update: GithubPullRequestUpdate = {
assignee: assignees?.[0]?.person ?? null
assignee: assignees?.[0] ?? null
}
await this.handleUpdate(externalData, derivedClient, update, account, prj, true)
break
@@ -326,27 +326,27 @@ 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<PersonId[]> {
// Find Assignees and reviewers
const ids: UserInfo[] = issue.reviewRequests.nodes.map((it: any) => it.requestedReviewer)
// const values: PersonAccount[] = []
const values: PersonId[] = []
// 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,
@@ -387,14 +387,14 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
info: DocSyncInfo
): Promise<DocumentUpdate<DocSyncInfo>> {
const account =
existing?.modifiedBy ?? (await this.provider.getAccount(pullRequestExternal.author))?._id ?? core.account.System
existing?.modifiedBy ?? (await this.provider.getAccount(pullRequestExternal.author)) ?? core.account.System
const accountGH =
info.lastGithubUser ?? (await this.provider.getAccount(pullRequestExternal.author))?._id ?? core.account.System
info.lastGithubUser ?? (await this.provider.getAccount(pullRequestExternal.author)) ?? core.account.System
// A target node id
const targetNodeId: string | undefined = info.targetNodeId as string
const okit = (await this.provider.getOctokit(account as PersonId)) ?? container.container.octokit
const okit = (await this.provider.getOctokit(account)) ?? container.container.octokit
const isProjectProjectTarget = target.target.projectNodeId === target.project.projectNodeId
const supportProjects =
@@ -452,13 +452,12 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
const assignees = await this.getAssignees(pullRequestExternal)
// TODO: FIXME
const reviewers: any = [] // await this.getReviewers(pullRequestExternal)
const reviewers: PersonId[] = await this.getReviewers(pullRequestExternal)
const latestReviews: LastReviewState[] = []
for (const d of pullRequestExternal.latestReviews?.nodes ?? []) {
const author = (await this.provider.getAccount(d.author))?._id
const author = await this.provider.getAccount(d.author)
if (author !== undefined) {
latestReviews.push({
state: toReviewState(d.state),
@@ -473,7 +472,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
pullRequestExternal.body,
this.stripGuestLink
),
assignee: assignees[0]?.person ?? null,
assignee: assignees[0] ?? null,
reviewers: reviewers.map((it: any) => it.person),
draft: pullRequestExternal.isDraft,
head: pullRequestExternal.headRef,
@@ -706,10 +705,10 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
}
}
const pendingOrDismissed = new Map<Ref<Person>, PullRequestReviewState>()
const pendingOrDismissedIds = new Map<PersonId, PullRequestReviewState>()
const approvedOrChangesRequested = new Map<Ref<Person>, PullRequestReviewState>()
const reviewStates = new Map<Ref<Person>, PullRequestReviewState[]>()
const approvedOrChangesRequested = new Map<PersonId, PullRequestReviewState>()
const reviewStates = new Map<PersonId, PullRequestReviewState[]>()
const sortedReviews: (Review & { date: number })[] = external.reviews.nodes
.filter((it) => it != null)
@@ -734,14 +733,18 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
continue
}
if (r.state === 'PENDING' || r.state === 'DISMISSED') {
pendingOrDismissed.set(rp.person, r.state)
pendingOrDismissedIds.set(rp, r.state)
}
if (r.state === 'APPROVED' || r.state === 'CHANGES_REQUESTED') {
approvedOrChangesRequested.set(rp.person, r.state)
approvedOrChangesRequested.set(rp, r.state)
}
reviewStates.set(rp.person, [...(reviewStates.get(rp.person) ?? []), r.state])
reviewStates.set(rp, [...(reviewStates.get(rp) ?? []), r.state])
}
const pendingOrDismissed = new Set(
await this.getPersonsFromId(Array.from(pendingOrDismissedIds.entries()).map((it) => it[0]))
)
for (const r of pullRequest.reviewers ?? []) {
// Find all related todos's
const todos = [...allTodos, ...removedTodos].filter((it) => it.user === r && it.purpose === 'review')
@@ -750,10 +753,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
const hasPending = todos.some((it) => it.doneOn !== null)
// Create review Todo, if missing.
if (
pullRequest.state === GithubPullRequestState.open ||
(!hasPending && pendingOrDismissed.get(r) !== undefined)
) {
if (pullRequest.state === GithubPullRequestState.open || (!hasPending && pendingOrDismissed.has(r))) {
if (todos.length === 0) {
await this.requestReview(client, pullRequest, external, r, account)
}
@@ -763,26 +763,28 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
// Handle change requests.
// If we have change requests pending, we need to create Todo to resolve them to author or assigned person, to resolve them.
const changeRequestPersons = new Set<Ref<Person>>()
const changeRequestPersonsIds = new Set<PersonId>()
const author = await this.provider.getAccount(external.author)
if (author !== undefined) {
changeRequestPersons.add(author.person)
changeRequestPersonsIds.add(author)
}
for (const au of external.assignees.nodes ?? []) {
const u = await this.provider.getAccount(au)
if (u !== undefined) {
changeRequestPersons.add(u.person)
changeRequestPersonsIds.add(u)
}
}
// Check review threads and create todo to resolve them.
const requestedIds: Ref<Person>[] = []
const changeRequestPersons = await this.getPersonsFromId(Array.from(changeRequestPersonsIds))
let allResolved = true
for (const r of external.reviewThreads.nodes) {
if (!r.isResolved) {
allResolved = false
for (const c of Array.from(changeRequestPersons)) {
for (const c of changeRequestPersons) {
// We need to add Todo to resolve PR.
const todos = [...allTodos, ...removedTodos].filter((it) => it.user === c && it.purpose === 'fix')
if (todos.length === 0) {
@@ -801,7 +803,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
for (const [, sst] of approvedOrChangesRequested.entries()) {
if (sst === 'CHANGES_REQUESTED') {
// We have changes requested and not resolved yet.
for (const c of Array.from(changeRequestPersons)) {
for (const c of changeRequestPersons) {
const todos = [...allTodos, ...removedTodos].filter((it) => it.user === c && it.purpose === 'fix')
if (todos.length === 0 && !requestedIds.includes(c)) {
requestedIds.push(c)
@@ -105,7 +105,7 @@ export class RepositorySyncMapper implements DocSyncManager {
async handleEvent<T>(integration: IntegrationContainer, derivedClient: TxOperations, evt: T): Promise<void> {
const event = evt as RepositoryEvent
const account = (await this.provider.getAccountU(event.sender))?._id ?? core.account.System
const account = (await this.provider.getAccountU(event.sender)) ?? core.account.System
switch (event.action) {
case 'created': {
await this.client.addCollection(
@@ -113,7 +113,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
return true
}
const account =
existing?.createdBy ?? (await this.provider.getAccountU(commentExternal.user))?._id ?? core.account.System
existing?.createdBy ?? (await this.provider.getAccountU(commentExternal.user)) ?? core.account.System
if (commentExternal !== undefined) {
try {
@@ -177,7 +177,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
repo: GithubIntegrationRepository,
integration: IntegrationContainer
): Promise<void> {
const account = (await this.provider.getAccountU(event.sender))?._id ?? core.account.System
const account = (await this.provider.getAccountU(event.sender)) ?? core.account.System
let externalData: ReviewCommentExternalData
try {
@@ -329,7 +329,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
const reviewComment = info.external as ReviewCommentExternalData
const account =
existing?.modifiedBy ?? (await this.provider.getAccount(reviewComment.author))?._id ?? core.account.System
existing?.modifiedBy ?? (await this.provider.getAccount(reviewComment.author)) ?? core.account.System
if (info.reviewThreadId === undefined && reviewComment.replyTo?.url !== undefined) {
const rthread = await derivedClient.findOne(github.class.GithubReviewComment, {
@@ -132,7 +132,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
return true
}
const account =
existing?.createdBy ?? (await this.provider.getAccountU(commentExternal.user))?._id ?? core.account.System
existing?.createdBy ?? (await this.provider.getAccountU(commentExternal.user)) ?? core.account.System
if (commentExternal !== undefined) {
try {
@@ -172,7 +172,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
repo: GithubIntegrationRepository,
integration: IntegrationContainer
): Promise<void> {
const account = (await this.provider.getAccountU(event.sender))?._id ?? core.account.System
const account = (await this.provider.getAccountU(event.sender)) ?? core.account.System
let externalData: ReviewThreadExternalData
try {
@@ -287,7 +287,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
// Use first comment as author, since github doesn't provide one.
const account =
existing?.modifiedBy ??
(await this.provider.getAccount(review.comments.nodes[0].author ?? null))?._id ??
(await this.provider.getAccount(review.comments.nodes[0].author ?? null)) ??
core.account.System
const messageData: ReviewThreadData = {
@@ -301,7 +301,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
originalLine: review.originalLine,
originalStartLine: review.originalStartLine,
path: review.path,
resolvedBy: (await this.provider.getAccount(review.resolvedBy))?._id ?? core.account.System,
resolvedBy: (await this.provider.getAccount(review.resolvedBy)) ?? core.account.System,
startDiffSide: review.startDiffSide
}
if (existing === undefined) {
@@ -110,7 +110,7 @@ export class ReviewSyncManager implements DocSyncManager {
return true
}
const account =
existing?.createdBy ?? (await this.provider.getAccountU(commentExternal.user))?._id ?? core.account.System
existing?.createdBy ?? (await this.provider.getAccountU(commentExternal.user)) ?? core.account.System
if (commentExternal !== undefined) {
try {
@@ -164,7 +164,7 @@ export class ReviewSyncManager implements DocSyncManager {
repo: GithubIntegrationRepository,
integration: IntegrationContainer
): Promise<void> {
const account = (await this.provider.getAccountU(event.sender))?._id ?? core.account.System
const account = (await this.provider.getAccountU(event.sender)) ?? core.account.System
let externalData: ReviewExternalData
try {
@@ -302,7 +302,7 @@ export class ReviewSyncManager implements DocSyncManager {
}
const review = info.external as ReviewExternalData
const account = existing?.modifiedBy ?? (await this.provider.getAccount(review.author))?._id ?? core.account.System
const account = existing?.modifiedBy ?? (await this.provider.getAccount(review.author)) ?? core.account.System
const messageData: ReviewData = {
body: await this.provider.getMarkupSafe(container.container, review.body),