mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-29 19:29:36 +02:00
qfix: Fix github measurements (#9816)
1. Fix github measurements 2. A bit more proper fix for caching of Octokit references Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
@@ -90,7 +90,7 @@ export class PlatformWorker {
|
||||
readonly ctx: MeasureContext,
|
||||
readonly app: App,
|
||||
readonly brandingMap: BrandingMap,
|
||||
readonly periodicSyncInterval = 10 * 60 * 1000 // 10 minutes
|
||||
readonly periodicSyncInterval = 24 * 60 * 60 * 1000 // 24 hours
|
||||
) {
|
||||
registerLoaders()
|
||||
}
|
||||
@@ -732,7 +732,7 @@ export class PlatformWorker {
|
||||
}
|
||||
}
|
||||
|
||||
async checkRefreshToken (ctx: MeasureContext, auth: GithubUserRecord, force: boolean = false): Promise<void> {
|
||||
async checkRefreshToken (ctx: MeasureContext, auth: GithubUserRecord, force: boolean = false): Promise<boolean> {
|
||||
if (auth.refreshToken != null && auth.expiresIn != null && auth.expiresIn < Date.now() / 1000) {
|
||||
const uri =
|
||||
'https://github.com/login/oauth/access_token?' +
|
||||
@@ -754,6 +754,7 @@ export class PlatformWorker {
|
||||
if (resultJson.error !== undefined) {
|
||||
// We need to clear github integration info.
|
||||
await this.revokeUserAuth(ctx, auth)
|
||||
return false
|
||||
} else {
|
||||
// Update okit
|
||||
const nowTime = Date.now() / 1000
|
||||
@@ -774,8 +775,10 @@ export class PlatformWorker {
|
||||
auth.scope = dta.scope
|
||||
|
||||
await this.userManager.updateUser(dta)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async getAccount (login: string): Promise<GithubUserRecord | undefined> {
|
||||
|
||||
@@ -80,8 +80,14 @@ export class CommentSyncManager implements DocSyncManager {
|
||||
await this.eventSync.get(event.issue.url)
|
||||
const promise = this.processEvent(ctx, event, derivedClient, integration)
|
||||
this.eventSync.set(event.issue.url, promise)
|
||||
await promise
|
||||
this.eventSync.delete(event.issue.url)
|
||||
try {
|
||||
await promise
|
||||
this.eventSync.delete(event.issue.url)
|
||||
} catch (err: any) {
|
||||
ctx.error('Error processing event', { error: err })
|
||||
} finally {
|
||||
this.eventSync.delete(event.issue.url)
|
||||
}
|
||||
}
|
||||
|
||||
async handleDelete (
|
||||
|
||||
@@ -22,7 +22,8 @@ import core, {
|
||||
Ref,
|
||||
Space,
|
||||
TxOperations,
|
||||
makeDocCollabId
|
||||
makeDocCollabId,
|
||||
withContext
|
||||
} from '@hcengineering/core'
|
||||
import github, { DocSyncInfo, GithubIntegrationRepository, GithubIssue, GithubProject } from '@hcengineering/github'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
@@ -112,6 +113,7 @@ export abstract class IssueSyncManagerBase {
|
||||
return socialIds.map((it) => it.attachedTo)
|
||||
}
|
||||
|
||||
@withContext('issues-handleUpdate')
|
||||
async handleUpdate (
|
||||
ctx: MeasureContext,
|
||||
external: IssueExternalData,
|
||||
@@ -135,12 +137,16 @@ export abstract class IssueSyncManagerBase {
|
||||
|
||||
syncData =
|
||||
syncData ??
|
||||
(await this.client.findOne(github.class.DocSyncInfo, { space: prj._id, url: (external.url ?? '').toLowerCase() }))
|
||||
(await ctx.with('findDocInfo', {}, (ctx) =>
|
||||
this.client.findOne(github.class.DocSyncInfo, { space: prj._id, url: (external.url ?? '').toLowerCase() })
|
||||
))
|
||||
|
||||
if (syncData !== undefined) {
|
||||
const doc: Issue | undefined = await this.client.findOne<Issue>(syncData.objectClass, {
|
||||
_id: syncData._id as unknown as Ref<Issue>
|
||||
})
|
||||
const doc: Issue | undefined = await ctx.with('find pull request', {}, (ctx) =>
|
||||
this.client.findOne<Issue>(syncData.objectClass, {
|
||||
_id: syncData._id as unknown as Ref<Issue>
|
||||
})
|
||||
)
|
||||
|
||||
// Use now as modified date for events.
|
||||
const lastModified = new Date().getTime()
|
||||
@@ -153,6 +159,7 @@ export abstract class IssueSyncManagerBase {
|
||||
) {
|
||||
try {
|
||||
const collabId = makeDocCollabId(doc, 'description')
|
||||
|
||||
await this.collaborator.updateMarkup(collabId, update.description)
|
||||
} catch (err: any) {
|
||||
Analytics.handleError(err)
|
||||
@@ -184,35 +191,39 @@ export abstract class IssueSyncManagerBase {
|
||||
await updateTodos.commit()
|
||||
}
|
||||
|
||||
await derivedClient.diffUpdate(
|
||||
syncData,
|
||||
{
|
||||
external,
|
||||
externalVersion: githubExternalSyncVersion,
|
||||
current: { ...syncData.current, ...update },
|
||||
needSync: needSync ? '' : githubSyncVersion, // No need to sync after operation.
|
||||
derivedVersion: '', // Check derived changes
|
||||
lastModified,
|
||||
lastGithubUser: account,
|
||||
...extraSyncUpdate
|
||||
},
|
||||
lastModified
|
||||
await ctx.with('diffUpdate syncData', {}, (ctx) =>
|
||||
derivedClient.diffUpdate(
|
||||
syncData,
|
||||
{
|
||||
external,
|
||||
externalVersion: githubExternalSyncVersion,
|
||||
current: { ...syncData.current, ...update },
|
||||
needSync: needSync ? '' : githubSyncVersion, // No need to sync after operation.
|
||||
derivedVersion: '', // Check derived changes
|
||||
lastModified,
|
||||
lastGithubUser: account,
|
||||
...extraSyncUpdate
|
||||
},
|
||||
lastModified
|
||||
)
|
||||
)
|
||||
await this.client.diffUpdate(doc, issueData, lastModified, account)
|
||||
await ctx.with('diffUpdate-issue', {}, (ctx) => this.client.diffUpdate(doc, issueData, lastModified, account))
|
||||
this.provider.sync()
|
||||
} else if (doc === undefined) {
|
||||
await derivedClient.diffUpdate(
|
||||
syncData,
|
||||
{
|
||||
external,
|
||||
externalVersion: githubExternalSyncVersion,
|
||||
needSync: '',
|
||||
derivedVersion: '', // Check derived changes
|
||||
lastModified,
|
||||
lastGithubUser: account,
|
||||
...extraSyncUpdate
|
||||
},
|
||||
lastModified
|
||||
await ctx.with('diffUpdate-syncData', {}, (ctx) =>
|
||||
derivedClient.diffUpdate(
|
||||
syncData,
|
||||
{
|
||||
external,
|
||||
externalVersion: githubExternalSyncVersion,
|
||||
needSync: '',
|
||||
derivedVersion: '', // Check derived changes
|
||||
lastModified,
|
||||
lastGithubUser: account,
|
||||
...extraSyncUpdate
|
||||
},
|
||||
lastModified
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -267,6 +278,7 @@ export abstract class IssueSyncManagerBase {
|
||||
info: DocSyncInfo
|
||||
): Promise<void>
|
||||
|
||||
@withContext('issues-handleDiffUpdate')
|
||||
async handleDiffUpdate (
|
||||
ctx: MeasureContext,
|
||||
container: ContainerFocus,
|
||||
@@ -282,7 +294,7 @@ export abstract class IssueSyncManagerBase {
|
||||
await ctx.with(
|
||||
'create mixin issue: GithubIssue',
|
||||
{},
|
||||
async () => {
|
||||
async (ctx) => {
|
||||
await this.client.createMixin<Issue, GithubIssue>(
|
||||
existing._id as Ref<GithubIssue>,
|
||||
existing._class,
|
||||
|
||||
@@ -111,11 +111,16 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
const urlId = issueEvent.issue.url
|
||||
|
||||
await syncRunner.exec(urlId, async () => {
|
||||
await this.processEvent(ctx, issueEvent, derivedClient, repository, integration, project)
|
||||
try {
|
||||
await this.processEvent(ctx, issueEvent, derivedClient, repository, integration, project)
|
||||
} catch (err: any) {
|
||||
ctx.error('Error processing event', { error: err })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@withContext('issues-processEvent')
|
||||
private async processEvent (
|
||||
ctx: MeasureContext,
|
||||
event: IssuesEvent,
|
||||
@@ -129,8 +134,9 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
let externalData: IssueExternalData | undefined
|
||||
if (event.action !== 'deleted') {
|
||||
try {
|
||||
const response: any = await integration.octokit?.graphql(
|
||||
`query listIssue($name: String!, $owner: String!, $issue: Int!) {
|
||||
const response: any = await ctx.with('graphql', {}, (ctx) =>
|
||||
integration.octokit?.graphql(
|
||||
`query listIssue($name: String!, $owner: String!, $issue: Int!) {
|
||||
repository(name: $name, owner: $owner) {
|
||||
issue(number: $issue) {
|
||||
${issueDetails(true)}
|
||||
@@ -138,11 +144,12 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
name: repo.name,
|
||||
owner: repo.owner?.login,
|
||||
issue: event.issue.number
|
||||
}
|
||||
{
|
||||
name: repo.name,
|
||||
owner: repo.owner?.login,
|
||||
issue: event.issue.number
|
||||
}
|
||||
)
|
||||
)
|
||||
externalData = response.repository.issue
|
||||
} catch (err: any) {
|
||||
@@ -351,7 +358,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
const description = await ctx.with(
|
||||
'query collaborative description',
|
||||
{},
|
||||
async () => {
|
||||
async (ctx) => {
|
||||
const collabId = makeDocCollabId(existing, 'description')
|
||||
return await this.collaborator.getMarkup(collabId, (existing as Issue).description)
|
||||
},
|
||||
@@ -367,7 +374,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
const createdIssueData = await ctx.with(
|
||||
'create github issue',
|
||||
{},
|
||||
async () => {
|
||||
async (ctx) => {
|
||||
this.createPromise = this.createGithubIssue(
|
||||
ctx,
|
||||
container,
|
||||
@@ -501,10 +508,11 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
await ctx.with(
|
||||
'create platform issue',
|
||||
{},
|
||||
async () => {
|
||||
async (ctx) => {
|
||||
const st = (await guessStatus(issueExternal, statuses))._id as Ref<Status>
|
||||
|
||||
await this.createNewIssue(
|
||||
ctx,
|
||||
info,
|
||||
accountGH,
|
||||
{
|
||||
@@ -555,7 +563,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
const updateResult = await ctx.with(
|
||||
'diff update',
|
||||
{},
|
||||
async () =>
|
||||
async (ctx) =>
|
||||
await this.handleDiffUpdate(
|
||||
ctx,
|
||||
container,
|
||||
@@ -655,7 +663,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
await ctx.with(
|
||||
'==> updateIssue',
|
||||
{},
|
||||
async () => {
|
||||
async (ctx) => {
|
||||
ctx.info('update fields', {
|
||||
url: issueExternal.url,
|
||||
...issueUpdate,
|
||||
@@ -696,7 +704,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
await ctx.with(
|
||||
'==> updateIssue',
|
||||
{},
|
||||
async () => {
|
||||
async (ctx) => {
|
||||
ctx.info('update fields', { ...issueUpdate, workspace: this.provider.getWorkspaceId() })
|
||||
if (isGHWriteAllowed()) {
|
||||
const hasOtherChanges = Object.keys(issueUpdate).length > 0
|
||||
@@ -807,7 +815,9 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
}
|
||||
}
|
||||
|
||||
@withContext('issues-createNewIssue')
|
||||
private async createNewIssue (
|
||||
ctx: MeasureContext,
|
||||
info: DocSyncInfo,
|
||||
account: PersonId,
|
||||
issueData: GithubIssueData & { status: Issue['status'] },
|
||||
@@ -930,7 +940,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan
|
||||
const response: any = await ctx.with(
|
||||
'graphql.listIssue',
|
||||
{},
|
||||
() =>
|
||||
(ctx) =>
|
||||
integration.octokit.graphql(
|
||||
`query listIssues {
|
||||
nodes(ids: [${idsp}] ) {
|
||||
|
||||
@@ -122,11 +122,16 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
|
||||
const url = event.pull_request.issue_url
|
||||
|
||||
await syncRunner.exec(url, async () => {
|
||||
await this.processEvent(ctx, event, derivedClient, repository, integration, project)
|
||||
try {
|
||||
await this.processEvent(ctx, event, derivedClient, repository, integration, project)
|
||||
} catch (err: any) {
|
||||
ctx.error('Error processing event', { error: err })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@withContext('pullrequests-processEvent')
|
||||
private async processEvent (
|
||||
ctx: MeasureContext,
|
||||
event: PullRequestEvent,
|
||||
@@ -139,8 +144,9 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
|
||||
|
||||
let externalData: PullRequestExternalData
|
||||
try {
|
||||
const response: any = await integration.octokit?.graphql(
|
||||
`query listIssue($name: String!, $owner: String!, $issue: Int!) {
|
||||
const response: any = await ctx.with('graphql', {}, (ctx) =>
|
||||
integration.octokit?.graphql(
|
||||
`query listIssue($name: String!, $owner: String!, $issue: Int!) {
|
||||
repository(name: $name, owner: $owner) {
|
||||
pullRequest(number: $issue) {
|
||||
${pullRequestDetails}
|
||||
@@ -148,11 +154,12 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
name: repo.name,
|
||||
owner: repo.owner?.login,
|
||||
issue: event.pull_request.number
|
||||
}
|
||||
{
|
||||
name: repo.name,
|
||||
owner: repo.owner?.login,
|
||||
issue: event.pull_request.number
|
||||
}
|
||||
)
|
||||
)
|
||||
externalData = response.repository.pullRequest
|
||||
} catch (err: any) {
|
||||
@@ -496,7 +503,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS
|
||||
}
|
||||
|
||||
// To sync reviews/review threads in case they are created before us.
|
||||
await syncChilds(info, this.client, derivedClient)
|
||||
await syncChilds(ctx, info, this.client, derivedClient)
|
||||
|
||||
return {
|
||||
needSync: '',
|
||||
|
||||
@@ -88,8 +88,13 @@ export class ReviewCommentSyncManager implements DocSyncManager {
|
||||
await this.eventSync.get(event.comment.html_url)
|
||||
const promise = this.processEvent(ctx, event, derivedClient, repository, integration)
|
||||
this.eventSync.set(event.comment.html_url, promise)
|
||||
await promise
|
||||
this.eventSync.delete(event.comment.html_url)
|
||||
try {
|
||||
await promise
|
||||
} catch (err: any) {
|
||||
ctx.error('Error processing event', { error: err })
|
||||
} finally {
|
||||
this.eventSync.delete(event.comment.html_url)
|
||||
}
|
||||
}
|
||||
|
||||
async handleDelete (
|
||||
@@ -364,7 +369,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
|
||||
}
|
||||
if (existing === undefined) {
|
||||
try {
|
||||
await this.createReviewComment(info, messageData, parent, reviewComment, account)
|
||||
await this.createReviewComment(ctx, info, messageData, parent, reviewComment, account)
|
||||
return { needSync: githubSyncVersion, current: messageData }
|
||||
} catch (err: any) {
|
||||
ctx.error('Error', { err })
|
||||
@@ -387,6 +392,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
|
||||
return { current: messageData, needSync: githubSyncVersion }
|
||||
}
|
||||
|
||||
@withContext('handleDiffUpdate-comment')
|
||||
private async handleDiffUpdate (
|
||||
ctx: MeasureContext,
|
||||
existing: Doc,
|
||||
@@ -458,7 +464,9 @@ export class ReviewCommentSyncManager implements DocSyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
@withContext('review-comments-createReviewComment')
|
||||
private async createReviewComment (
|
||||
ctx: MeasureContext,
|
||||
info: DocSyncInfo,
|
||||
messageData: ReviewCommentData,
|
||||
parent: DocSyncInfo,
|
||||
@@ -482,6 +490,7 @@ export class ReviewCommentSyncManager implements DocSyncManager {
|
||||
)
|
||||
}
|
||||
|
||||
@withContext('review-comments-create')
|
||||
async createGithubReviewComment (
|
||||
ctx: MeasureContext,
|
||||
container: ContainerFocus,
|
||||
|
||||
@@ -108,8 +108,13 @@ export class ReviewThreadSyncManager implements DocSyncManager {
|
||||
await this.eventSync.get(event.thread.node_id)
|
||||
const promise = this.processEvent(ctx, event, derivedClient, repository, integration)
|
||||
this.eventSync.set(event.thread.node_id, promise)
|
||||
await promise
|
||||
this.eventSync.delete(event.thread.node_id)
|
||||
try {
|
||||
await promise
|
||||
} catch (err: any) {
|
||||
ctx.error('Error processing event', { error: err })
|
||||
} finally {
|
||||
this.eventSync.delete(event.thread.node_id)
|
||||
}
|
||||
}
|
||||
|
||||
async handleDelete (
|
||||
@@ -311,7 +316,7 @@ export class ReviewThreadSyncManager implements DocSyncManager {
|
||||
await this.createReviewThread(info, messageData, parent, review, account)
|
||||
|
||||
// We need trigger comments, if their sync data created before
|
||||
await syncChilds(info, this.client, derivedClient)
|
||||
await syncChilds(ctx, info, this.client, derivedClient)
|
||||
return { needSync: githubSyncVersion, current: messageData }
|
||||
} catch (err: any) {
|
||||
ctx.error('Error', { err })
|
||||
|
||||
@@ -86,8 +86,13 @@ export class ReviewSyncManager implements DocSyncManager {
|
||||
await this.eventSync.get(event.review.html_url)
|
||||
const promise = this.processEvent(ctx, event, derivedClient, repository, integration)
|
||||
this.eventSync.set(event.review.html_url, promise)
|
||||
await promise
|
||||
this.eventSync.delete(event.review.html_url)
|
||||
try {
|
||||
await promise
|
||||
} catch (err: any) {
|
||||
ctx.error('Error processing event', { error: err })
|
||||
} finally {
|
||||
this.eventSync.delete(event.review.html_url)
|
||||
}
|
||||
}
|
||||
|
||||
async handleDelete (
|
||||
@@ -318,9 +323,9 @@ export class ReviewSyncManager implements DocSyncManager {
|
||||
}
|
||||
if (existing === undefined) {
|
||||
try {
|
||||
await this.createReview(info, messageData, parent, review, account)
|
||||
await this.createReview(ctx, info, messageData, parent, review, account)
|
||||
|
||||
await syncChilds(info, this.client, derivedClient)
|
||||
await syncChilds(ctx, info, this.client, derivedClient)
|
||||
return { needSync: githubSyncVersion, current: messageData }
|
||||
} catch (err: any) {
|
||||
ctx.error('Error', { err })
|
||||
@@ -328,12 +333,14 @@ export class ReviewSyncManager implements DocSyncManager {
|
||||
return { needSync: githubSyncVersion, error: errorToObj(err) }
|
||||
}
|
||||
} else {
|
||||
await this.handleDiffUpdate(existing, info, messageData, container, parent, review, account)
|
||||
await this.handleDiffUpdate(ctx, existing, info, messageData, container, parent, review, account)
|
||||
}
|
||||
return { current: messageData, needSync: githubSyncVersion }
|
||||
}
|
||||
|
||||
@withContext('reviews-handleDiffUpdate')
|
||||
private async handleDiffUpdate (
|
||||
ctx: MeasureContext,
|
||||
existing: Doc,
|
||||
info: DocSyncInfo,
|
||||
reviewData: ReviewData,
|
||||
@@ -379,7 +386,9 @@ export class ReviewSyncManager implements DocSyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
@withContext('reviews-createReview')
|
||||
private async createReview (
|
||||
ctx: MeasureContext,
|
||||
info: DocSyncInfo,
|
||||
messageData: ReviewData,
|
||||
parent: DocSyncInfo,
|
||||
@@ -403,6 +412,7 @@ export class ReviewSyncManager implements DocSyncManager {
|
||||
)
|
||||
}
|
||||
|
||||
@withContext('reviews-createGithubReview')
|
||||
async createGithubReview (
|
||||
ctx: MeasureContext,
|
||||
container: ContainerFocus,
|
||||
|
||||
@@ -196,9 +196,11 @@ export class SyncRunner {
|
||||
id,
|
||||
promise.then(() => {})
|
||||
)
|
||||
const result = await promise
|
||||
this.eventSync.delete(id)
|
||||
return result
|
||||
try {
|
||||
return await promise
|
||||
} finally {
|
||||
this.eventSync.delete(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,13 +330,20 @@ export function compareMarkdown (a: string, b: string): boolean {
|
||||
return na === nb
|
||||
}
|
||||
|
||||
export async function syncChilds (info: DocSyncInfo, client: TxOperations, derivedClient: TxOperations): Promise<void> {
|
||||
const childInfos = await client.findAll(github.class.DocSyncInfo, { parent: info.url.toLowerCase() })
|
||||
export async function syncChilds (
|
||||
ctx: MeasureContext,
|
||||
info: DocSyncInfo,
|
||||
client: TxOperations,
|
||||
derivedClient: TxOperations
|
||||
): Promise<void> {
|
||||
const childInfos = await ctx.with('syncChilds-find', {}, () =>
|
||||
client.findAll(github.class.DocSyncInfo, { parent: info.url.toLowerCase() })
|
||||
)
|
||||
if (childInfos.length > 0) {
|
||||
const ops = derivedClient.apply()
|
||||
for (const child of childInfos) {
|
||||
await ops?.update(child, { needSync: '' })
|
||||
}
|
||||
await ops.commit()
|
||||
await ctx.with('sync-child-trigger', {}, () => ops.commit())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,4 +224,6 @@ export interface GithubUserRecord {
|
||||
scope?: string
|
||||
error?: string | null
|
||||
accounts: Record<WorkspaceUuid, PersonId>
|
||||
|
||||
octokit?: Octokit
|
||||
}
|
||||
|
||||
@@ -45,6 +45,15 @@ export class UserManager {
|
||||
}
|
||||
|
||||
failedRefs = new Set<string>()
|
||||
|
||||
cacheRecord (workspace: WorkspaceUuid, ref: PersonId, record: GithubUserRecord): void {
|
||||
if (this.refUserCache.size > 1000) {
|
||||
this.refUserCache.clear()
|
||||
}
|
||||
const key = `${workspace}.${ref}`
|
||||
this.refUserCache.set(key, record)
|
||||
}
|
||||
|
||||
async getAccountByRef (
|
||||
ctx: MeasureContext,
|
||||
workspace: WorkspaceUuid,
|
||||
|
||||
@@ -560,13 +560,19 @@ export class GithubWorker implements IntegrationManager {
|
||||
|
||||
async syncUserData (ctx: MeasureContext): Promise<void> {
|
||||
// Let's sync information about users and send some details
|
||||
const accounts = await this._client.findAll(contact.class.SocialIdentity, {
|
||||
type: SocialIdType.GITHUB
|
||||
})
|
||||
const userAuths = await this._client.findAll(github.class.GithubAuthentication, {})
|
||||
const persons = await this._client.findAll(contact.class.Person, {
|
||||
_id: { $in: accounts.map((it) => it.attachedTo) }
|
||||
})
|
||||
const accounts = await ctx.with('find-social-id', {}, () =>
|
||||
this._client.findAll(contact.class.SocialIdentity, {
|
||||
type: SocialIdType.GITHUB
|
||||
})
|
||||
)
|
||||
const userAuths = await ctx.with('find-github-auths', {}, () =>
|
||||
this._client.findAll(github.class.GithubAuthentication, {})
|
||||
)
|
||||
const persons = await ctx.with('find-persons', {}, () =>
|
||||
this._client.findAll(contact.class.Person, {
|
||||
_id: { $in: accounts.map((it) => it.attachedTo) }
|
||||
})
|
||||
)
|
||||
for (const account of accounts) {
|
||||
const userAuth = userAuths.find((it) => it.login === account.value)
|
||||
const person = persons.find((it) => account?.attachedTo)
|
||||
@@ -611,13 +617,18 @@ export class GithubWorker implements IntegrationManager {
|
||||
async getOctokit (ctx: MeasureContext, account: PersonId): Promise<Octokit | undefined> {
|
||||
let record = await this.platform.getAccountByRef(this.workspace.uuid, account)
|
||||
|
||||
const accountRef = await this._client.findOne(contact.class.SocialIdentity, { _id: account as any })
|
||||
if (record === undefined) {
|
||||
const accountRef = await ctx.with('find-social-id', {}, () =>
|
||||
this._client.findOne(contact.class.SocialIdentity, { _id: account as any })
|
||||
)
|
||||
if (accountRef !== undefined) {
|
||||
const accounts = await this._client.findAll(contact.class.SocialIdentity, { attachedTo: accountRef.attachedTo })
|
||||
const accounts = await ctx.with('find-accounts', {}, () =>
|
||||
this._client.findAll(contact.class.SocialIdentity, { attachedTo: accountRef.attachedTo })
|
||||
)
|
||||
for (const aa of accounts) {
|
||||
record = await this.platform.getAccountByRef(this.workspace.uuid, aa._id)
|
||||
if (record !== undefined) {
|
||||
this.platform.userManager.cacheRecord(this.workspace.uuid, account, record)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -625,33 +636,55 @@ export class GithubWorker implements IntegrationManager {
|
||||
}
|
||||
// Check and refresh token if required.
|
||||
if (record !== undefined) {
|
||||
this.ctx.info('get octokit', { account, recordId: record._id, workspace: this.workspace.uuid })
|
||||
await this.platform.checkRefreshToken(ctx, record)
|
||||
return new Octokit({
|
||||
auth: record.token,
|
||||
client_id: config.ClientID,
|
||||
client_secret: config.ClientSecret
|
||||
})
|
||||
ctx.info('get octokit', { account, recordId: record._id, workspace: this.workspace.uuid })
|
||||
if (!(await this.platform.checkRefreshToken(ctx, record))) {
|
||||
record.octokit = undefined
|
||||
}
|
||||
if (record.octokit !== undefined) {
|
||||
return record.octokit
|
||||
}
|
||||
|
||||
record.octokit = ctx.withSync(
|
||||
'create-octokit',
|
||||
{},
|
||||
() =>
|
||||
new Octokit({
|
||||
auth: record.token,
|
||||
client_id: config.ClientID,
|
||||
client_secret: config.ClientSecret
|
||||
})
|
||||
)
|
||||
return record.octokit
|
||||
}
|
||||
|
||||
// We need to inform user, he need to authorize this account with github.
|
||||
// TODO: Inform user it need authenticsion
|
||||
if (!this.authRequestSend.has(account)) {
|
||||
this.authRequestSend.add(account)
|
||||
const socialId = await this._client.findOne(contact.class.SocialIdentity, { _id: account as any })
|
||||
const socialId = await ctx.with('find-social-id', {}, () =>
|
||||
this._client.findOne(contact.class.SocialIdentity, { _id: account as any })
|
||||
)
|
||||
if (socialId !== undefined) {
|
||||
const personSpace = await this.liveQuery.findOne(contact.class.PersonSpace, { person: socialId.attachedTo })
|
||||
const person = await this._client.findOne(contact.mixin.Employee, { _id: socialId.attachedTo as Ref<Employee> })
|
||||
const personSpace = await ctx.with('find-person-space', {}, () =>
|
||||
this.liveQuery.findOne(contact.class.PersonSpace, { person: socialId.attachedTo })
|
||||
)
|
||||
const person = await ctx.with('find-person', {}, () =>
|
||||
this._client.findOne(contact.mixin.Employee, { _id: socialId.attachedTo as Ref<Employee> })
|
||||
)
|
||||
if (personSpace !== undefined && person !== undefined) {
|
||||
// We need to remove if user has authentication in workspace but doesn't have a record.
|
||||
|
||||
const allSocialId = await this._client.findAll(contact.class.SocialIdentity, {
|
||||
attachedTo: personSpace.person
|
||||
})
|
||||
const allSocialId = await ctx.with('find-all-social-ids', {}, () =>
|
||||
this._client.findAll(contact.class.SocialIdentity, {
|
||||
attachedTo: personSpace.person
|
||||
})
|
||||
)
|
||||
|
||||
const authentications = await this.liveQuery.findAll(github.class.GithubAuthentication, {
|
||||
createdBy: { $in: allSocialId.map((it) => it._id) }
|
||||
})
|
||||
const authentications = await ctx.with('find-authentications', {}, () =>
|
||||
this.liveQuery.findAll(github.class.GithubAuthentication, {
|
||||
createdBy: { $in: allSocialId.map((it) => it._id) }
|
||||
})
|
||||
)
|
||||
for (const auth of authentications) {
|
||||
await this._client.remove(auth)
|
||||
}
|
||||
@@ -1026,7 +1059,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
continue
|
||||
}
|
||||
|
||||
this.ctx.info('External Syncing', {
|
||||
ctx.info('External Syncing', {
|
||||
name: repo.name,
|
||||
prj: prj.name,
|
||||
field,
|
||||
@@ -1042,7 +1075,7 @@ export class GithubWorker implements IntegrationManager {
|
||||
await mapper?.externalSync(ctx, integration, derivedClient, field, _docs, repo, prj)
|
||||
} catch (err: any) {
|
||||
Analytics.handleError(err)
|
||||
this.ctx.error('failed to perform external sync', err)
|
||||
ctx.error('failed to perform external sync', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1578,10 +1611,10 @@ export class GithubWorker implements IntegrationManager {
|
||||
if (this.closing) {
|
||||
break
|
||||
}
|
||||
await this.ctx.with(
|
||||
await ctx.with(
|
||||
'external sync',
|
||||
{},
|
||||
async () => {
|
||||
async (ctx) => {
|
||||
const enabled = integration.enabled && integration.octokit !== undefined
|
||||
|
||||
const upd: DocumentUpdate<GithubIntegration> = {}
|
||||
@@ -1622,10 +1655,8 @@ export class GithubWorker implements IntegrationManager {
|
||||
if (this.closing) {
|
||||
break
|
||||
}
|
||||
const withError = await derivedClient.findAll<any>(
|
||||
github.class.DocSyncInfo,
|
||||
{ error: { $ne: null }, url: null },
|
||||
{ limit: 50 }
|
||||
const withError = await ctx.with('find-docSyncInfo', {}, () =>
|
||||
derivedClient.findAll<any>(github.class.DocSyncInfo, { error: { $ne: null }, url: null }, { limit: 50 })
|
||||
)
|
||||
|
||||
if (withError.length === 0) {
|
||||
@@ -1642,10 +1673,8 @@ export class GithubWorker implements IntegrationManager {
|
||||
if (this.closing) {
|
||||
break
|
||||
}
|
||||
const withError = await derivedClient.findAll<any>(
|
||||
github.class.DocSyncInfo,
|
||||
{ error: { $ne: null } },
|
||||
{ limit: 50 }
|
||||
const withError = await ctx.with('find-docSyncInfo-errors', {}, () =>
|
||||
derivedClient.findAll<any>(github.class.DocSyncInfo, { error: { $ne: null } }, { limit: 50 })
|
||||
)
|
||||
|
||||
if (withError.length === 0) {
|
||||
@@ -1666,17 +1695,17 @@ export class GithubWorker implements IntegrationManager {
|
||||
|
||||
await ops.update(d, { error: null, needSync: skipError ? githubSyncVersion : '' })
|
||||
}
|
||||
await ops.commit()
|
||||
await ctx.with('commit-errors-docsync-info', {}, () => ops.commit())
|
||||
}
|
||||
|
||||
for (const { _class, mapper } of this.mappers) {
|
||||
if (this.closing) {
|
||||
break
|
||||
}
|
||||
await this.ctx.with(
|
||||
'external sync',
|
||||
{ _class: _class.join(', ') },
|
||||
async () => {
|
||||
await ctx.with(
|
||||
'mapper external sync',
|
||||
{},
|
||||
async (ctx) => {
|
||||
await mapper.externalFullSync(ctx, integration, derivedClient, _projects, _repositories)
|
||||
},
|
||||
{ installation: integration.installationName, workspace: this.workspace.uuid },
|
||||
@@ -1810,11 +1839,14 @@ export async function syncUser (
|
||||
client: TxOperations,
|
||||
account: PersonId
|
||||
): Promise<void> {
|
||||
const okit = new Octokit({
|
||||
auth: record.token,
|
||||
client_id: config.ClientID,
|
||||
client_secret: config.ClientSecret
|
||||
})
|
||||
const okit =
|
||||
record.octokit ??
|
||||
new Octokit({
|
||||
auth: record.token,
|
||||
client_id: config.ClientID,
|
||||
client_secret: config.ClientSecret
|
||||
})
|
||||
record.octokit = okit
|
||||
|
||||
const details = await fetchViewerDetails(okit)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user