mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-31 12:19:47 +02:00
UBER-1233: Milestone related fixes (#7614)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
@@ -27,6 +27,8 @@ interface Config {
|
||||
|
||||
SentryDSN: string
|
||||
BrandingPath: string
|
||||
|
||||
WorkspaceInactivityInterval: number // Interval in days to stop workspace synchronization if not visited
|
||||
}
|
||||
|
||||
const envMap: { [key in keyof Config]: string } = {
|
||||
@@ -51,7 +53,9 @@ const envMap: { [key in keyof Config]: string } = {
|
||||
CollaboratorURL: 'COLLABORATOR_URL',
|
||||
|
||||
SentryDSN: 'SENTRY_DSN',
|
||||
BrandingPath: 'BRANDING_PATH'
|
||||
BrandingPath: 'BRANDING_PATH',
|
||||
|
||||
WorkspaceInactivityInterval: 'WORKSPACE_INACTIVITY_INTERVAL'
|
||||
}
|
||||
|
||||
const required: Array<keyof Config> = [
|
||||
@@ -96,7 +100,8 @@ const config: Config = (() => {
|
||||
CollaboratorURL: process.env[envMap.CollaboratorURL],
|
||||
|
||||
SentryDSN: process.env[envMap.SentryDSN],
|
||||
BrandingPath: process.env[envMap.BrandingPath] ?? ''
|
||||
BrandingPath: process.env[envMap.BrandingPath] ?? '',
|
||||
WorkspaceInactivityInterval: parseInt(process.env[envMap.WorkspaceInactivityInterval] ?? '5') // In days
|
||||
}
|
||||
|
||||
const missingEnv = required.filter((key) => params[key] === undefined).map((key) => envMap[key])
|
||||
|
||||
@@ -697,6 +697,35 @@ export class PlatformWorker {
|
||||
return Array.from(workspaces)
|
||||
}
|
||||
|
||||
async checkWorkspaceIsActive (token: string, workspace: string): Promise<ClientWorkspaceInfo | undefined> {
|
||||
let workspaceInfo: ClientWorkspaceInfo | undefined
|
||||
try {
|
||||
workspaceInfo = await getWorkspaceInfo(token)
|
||||
} catch (err: any) {
|
||||
this.ctx.error('Workspace not found:', { workspace })
|
||||
return
|
||||
}
|
||||
if (workspaceInfo?.workspace === undefined) {
|
||||
this.ctx.error('No workspace exists for workspaceId', { workspace })
|
||||
return
|
||||
}
|
||||
if (!isActiveMode(workspaceInfo?.mode)) {
|
||||
this.ctx.warn('Workspace is in maitenance, skipping for now.', { workspace })
|
||||
return
|
||||
}
|
||||
if (workspaceInfo?.disabled === true) {
|
||||
this.ctx.warn('Workspace is disabled', { workspace })
|
||||
return
|
||||
}
|
||||
const lastVisit = (Date.now() - workspaceInfo.lastVisit) / (3600 * 24 * 1000) // In days
|
||||
|
||||
if (config.WorkspaceInactivityInterval > 0 && lastVisit > config.WorkspaceInactivityInterval) {
|
||||
this.ctx.warn('Workspace is inactive for too long, skipping for now.', { workspace })
|
||||
return
|
||||
}
|
||||
return workspaceInfo
|
||||
}
|
||||
|
||||
private async checkWorkspaces (): Promise<boolean> {
|
||||
this.ctx.info('************************* Check workspaces ************************* ', {
|
||||
workspaces: this.clients.size
|
||||
@@ -737,27 +766,11 @@ export class PlatformWorker {
|
||||
},
|
||||
{ mode: 'github' }
|
||||
)
|
||||
let workspaceInfo: ClientWorkspaceInfo | undefined
|
||||
try {
|
||||
workspaceInfo = await getWorkspaceInfo(token, true)
|
||||
} catch (err: any) {
|
||||
this.ctx.error('Workspace not found:', { workspace })
|
||||
const workspaceInfo = await this.checkWorkspaceIsActive(token, workspace)
|
||||
if (workspaceInfo === undefined) {
|
||||
errors++
|
||||
return
|
||||
}
|
||||
if (workspaceInfo?.workspace === undefined) {
|
||||
this.ctx.error('No workspace exists for workspaceId', { workspace })
|
||||
errors++
|
||||
return
|
||||
}
|
||||
if (!isActiveMode(workspaceInfo?.mode)) {
|
||||
this.ctx.warn('Workspace is in maitenance, skipping for now.', { workspace })
|
||||
return
|
||||
}
|
||||
if (workspaceInfo?.disabled === true) {
|
||||
this.ctx.warn('Workspace is disabled', { workspace })
|
||||
return
|
||||
}
|
||||
try {
|
||||
const branding = Object.values(this.brandingMap).find((b) => b.key === workspaceInfo?.branding) ?? null
|
||||
const workerCtx = this.ctx.newChild('worker', { workspace: workspaceInfo.workspace }, {})
|
||||
@@ -786,6 +799,14 @@ export class PlatformWorker {
|
||||
if (event === ClientConnectEvent.Refresh || event === ClientConnectEvent.Upgraded) {
|
||||
void this.clients.get(workspace)?.refreshClient(event === ClientConnectEvent.Upgraded)
|
||||
}
|
||||
// We need to check if workspace is inactive
|
||||
void this.checkWorkspaceIsActive(token, workspace).then((res) => {
|
||||
if (res === undefined) {
|
||||
this.ctx.warn('Workspace is inactive, removing from clients list.', { workspace })
|
||||
this.clients.delete(workspace)
|
||||
void worker?.close()
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
if (worker !== undefined) {
|
||||
|
||||
@@ -35,7 +35,8 @@ import github, {
|
||||
} from '@hcengineering/github'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { LiveQuery } from '@hcengineering/query'
|
||||
import task, { TaskType } from '@hcengineering/task'
|
||||
import { getPublicLink } from '@hcengineering/server-guest-resources'
|
||||
import task, { TaskType, type Task } from '@hcengineering/task'
|
||||
import { MarkupNode, MarkupNodeType, areEqualMarkups, markupToJSON, traverseNode } from '@hcengineering/text'
|
||||
import time, { type ToDo } from '@hcengineering/time'
|
||||
import tracker, { Issue, IssuePriority } from '@hcengineering/tracker'
|
||||
@@ -59,7 +60,7 @@ import {
|
||||
projectValue,
|
||||
supportedGithubTypes
|
||||
} from './githubTypes'
|
||||
import { appendGuestLink, stripGuestLink } from './guest'
|
||||
import { stripGuestLink } from './guest'
|
||||
import { syncConfig } from './syncConfig'
|
||||
import {
|
||||
collectUpdate,
|
||||
@@ -998,13 +999,7 @@ export abstract class IssueSyncManagerBase {
|
||||
}
|
||||
if (platformUpdate.description != null) {
|
||||
// Need to convert to markdown
|
||||
const pp = async (nodes: MarkupNode): Promise<void> => {
|
||||
await appendGuestLink(this.client, doc, nodes, this.provider.getWorkspaceId(), this.provider.getBranding())
|
||||
}
|
||||
issueUpdate.body = await this.provider.getMarkdown(
|
||||
platformUpdate.description ?? '',
|
||||
info.allowOpenInHuly === true ? pp : undefined
|
||||
)
|
||||
issueUpdate.body = await this.provider.getMarkdown(platformUpdate.description ?? '')
|
||||
issueData.description = await this.provider.getMarkup(
|
||||
container.container,
|
||||
issueUpdate.body ?? '',
|
||||
@@ -1270,7 +1265,7 @@ export abstract class IssueSyncManagerBase {
|
||||
|
||||
if (existing !== undefined && deleteExisting) {
|
||||
const childItems = await derivedClient.findAll(github.class.DocSyncInfo, {
|
||||
parentUrl: (issueExternal.url ?? '').toLowerCase()
|
||||
parent: (issueExternal.url ?? '').toLowerCase()
|
||||
})
|
||||
for (const u of childItems) {
|
||||
// We need just to clean all of them, since child's for issue are comments for now.
|
||||
@@ -1315,4 +1310,35 @@ export abstract class IssueSyncManagerBase {
|
||||
this.provider.sync()
|
||||
}
|
||||
}
|
||||
|
||||
async addHulyLink (
|
||||
info: DocSyncInfo,
|
||||
syncResult: DocumentUpdate<DocSyncInfo>,
|
||||
object: Doc,
|
||||
external: IssueExternalData,
|
||||
container: ContainerFocus
|
||||
): Promise<void> {
|
||||
const repository = await this.provider.getRepositoryById(info.repository)
|
||||
if (repository !== undefined) {
|
||||
syncResult.addHulyLink = false
|
||||
const publicLink = await getPublicLink(
|
||||
object,
|
||||
this.client,
|
||||
this.provider.getWorkspaceId(),
|
||||
false,
|
||||
this.provider.getBranding()
|
||||
)
|
||||
// We need to create comment on Github about issue is connected.
|
||||
await container.container.octokit.rest.issues.createComment({
|
||||
owner: repository.owner?.login as string,
|
||||
repo: repository.name,
|
||||
issue_number: external.number,
|
||||
|
||||
body: `<p>Connected to <b><a href="${publicLink}">Huly®: ${(object as Task).identifier}</a></b></p>`,
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ import core, {
|
||||
TxOperations,
|
||||
cutObjectArray,
|
||||
generateId,
|
||||
makeDocCollabId,
|
||||
makeCollabId,
|
||||
makeCollabJsonId,
|
||||
makeCollabId
|
||||
makeDocCollabId
|
||||
} from '@hcengineering/core'
|
||||
import github, {
|
||||
DocSyncInfo,
|
||||
@@ -46,7 +46,6 @@ import {
|
||||
githubSyncVersion
|
||||
} from '../types'
|
||||
import { IssueExternalData, issueDetails } from './githubTypes'
|
||||
import { appendGuestLink } from './guest'
|
||||
import { GithubIssueData, IssueSyncManagerBase, IssueSyncTarget, IssueUpdate, WithMarkup } from './issueBase'
|
||||
import { syncConfig } from './syncConfig'
|
||||
import { getSince, gqlp, guessStatus, isGHWriteAllowed, syncRunner } from './utils'
|
||||
@@ -307,7 +306,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
external: issueExternal,
|
||||
externalVersion: githubExternalSyncVersion,
|
||||
lastModified: new Date(issueExternal.updatedAt).getTime(),
|
||||
allowOpenInHuly: true
|
||||
addHulyLink: true
|
||||
})
|
||||
|
||||
// We need trigger comments, if their sync data created before
|
||||
@@ -332,6 +331,9 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
if (container?.container === undefined) {
|
||||
return { needSync: githubSyncVersion }
|
||||
}
|
||||
|
||||
let needCreateConnectedAtHuly = info.addHulyLink === true
|
||||
|
||||
if (
|
||||
(container.project.projectNodeId === undefined ||
|
||||
!container.container.projectStructure.has(container.project._id)) &&
|
||||
@@ -413,12 +415,13 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
url: issueExternal.url.toLowerCase(),
|
||||
githubNumber: issueExternal.number,
|
||||
lastModified: new Date(issueExternal.updatedAt).getTime(),
|
||||
allowOpenInHuly: true,
|
||||
addHulyLink: false, // Do not need, since we create comment on Github about issue is connected.
|
||||
current: {
|
||||
title: issueExternal.title,
|
||||
description: await this.provider.getMarkup(container.container, issueExternal.body, this.stripGuestLink)
|
||||
}
|
||||
}
|
||||
needCreateConnectedAtHuly = true
|
||||
await derivedClient.update(info, update)
|
||||
info.external = update.external
|
||||
info.externalVersion = update.externalVersion
|
||||
@@ -462,6 +465,11 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (existing !== undefined && issueExternal !== undefined && needCreateConnectedAtHuly) {
|
||||
await this.addHulyLink(info, syncResult, existing, issueExternal, container)
|
||||
}
|
||||
|
||||
return {
|
||||
...syncResult,
|
||||
issueExternal,
|
||||
@@ -837,10 +845,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
}
|
||||
}`
|
||||
|
||||
const body =
|
||||
(await this.provider.getMarkdown(existingIssue.description, async (nodes) => {
|
||||
await appendGuestLink(this.client, existing, nodes, this.provider.getWorkspaceId(), this.provider.getBranding())
|
||||
})) ?? ''
|
||||
const body = (await this.provider.getMarkdown(existingIssue.description)) ?? ''
|
||||
if (isGHWriteAllowed()) {
|
||||
const response:
|
||||
| {
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
ExternalSyncField,
|
||||
IntegrationContainer,
|
||||
IntegrationManager,
|
||||
githubExternalSyncVersion,
|
||||
githubSyncVersion
|
||||
} from '../types'
|
||||
import {
|
||||
@@ -353,7 +354,16 @@ export class ProjectsSyncManager implements DocSyncManager {
|
||||
syncDocs: DocSyncInfo[],
|
||||
repository: GithubIntegrationRepository,
|
||||
project: GithubProject
|
||||
): Promise<void> {}
|
||||
): Promise<void> {
|
||||
for (const d of syncDocs) {
|
||||
if (d.objectClass === tracker.class.Milestone) {
|
||||
// no external data for doc
|
||||
await derivedClient.update<DocSyncInfo>(d, {
|
||||
externalVersion: githubExternalSyncVersion
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositoryDisabled (integration: IntegrationContainer, repo: GithubIntegrationRepository): void {
|
||||
integration.synchronized.delete(`${repo._id}:issues`)
|
||||
|
||||
@@ -357,7 +357,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
|
||||
external: pullRequestExternal,
|
||||
externalVersion: githubExternalSyncVersion,
|
||||
derivedVersion: '',
|
||||
allowOpenInHuly: true,
|
||||
addHulyLink: true,
|
||||
lastModified,
|
||||
lastGithubUser: account
|
||||
})
|
||||
@@ -961,6 +961,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
|
||||
if (container?.container === undefined) {
|
||||
return { needSync: githubSyncVersion }
|
||||
}
|
||||
const needCreateConnectedAtHuly = info.addHulyLink === true
|
||||
if (
|
||||
(container.project.projectNodeId === undefined ||
|
||||
!container.container.projectStructure.has(container.project._id)) &&
|
||||
@@ -991,6 +992,9 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
|
||||
|
||||
const syncResult = await this.syncToTarget(target, container, existing, pullRequestExternal, derivedClient, info)
|
||||
|
||||
if (existing !== undefined && pullRequestExternal !== undefined && needCreateConnectedAtHuly) {
|
||||
await this.addHulyLink(info, syncResult, existing, pullRequestExternal, container)
|
||||
}
|
||||
return {
|
||||
...syncResult,
|
||||
targetNodeId: target.target.projectNodeId
|
||||
|
||||
@@ -1202,7 +1202,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
const projects: GithubProject[] = []
|
||||
const repositories: GithubIntegrationRepository[] = []
|
||||
|
||||
const allProjects = await this.liveQuery.queryFind<GithubProject>(github.mixin.GithubProject, {})
|
||||
const allProjects = await this.liveQuery.queryFind<GithubProject>(github.mixin.GithubProject, { archived: false })
|
||||
const allRepositories = await this.liveQuery.queryFind(github.class.GithubIntegrationRepository, { enabled: true })
|
||||
|
||||
for (const it of Array.from(this.integrations.values())) {
|
||||
|
||||
Reference in New Issue
Block a user