From 503ceeeab6e3a9f43bacb944ca74a210be215fbb Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Thu, 14 Nov 2024 20:00:21 +0700 Subject: [PATCH 01/17] Cockroach DB in dev config (#7170) Signed-off-by: Andrey Sobolev --- dev/docker-compose.yaml | 98 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 6 deletions(-) diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index 2ad74469fb..90db31d1bb 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -29,6 +29,14 @@ services: ports: - 5432:5432 restart: unless-stopped + cockroach: + image: cockroachdb/cockroach:latest-v24.2 + ports: + - "26257:26257" + - "8089:8080" + command: start-single-node --insecure + volumes: + - cockroach_db:/cockroach/cockroach-data" minio: image: 'minio/minio' command: server /data --address ":9000" --console-address ":9001" @@ -78,8 +86,8 @@ services: # - DB_URL=postgresql://postgres:example@postgres:5432 - DB_URL=${MONGO_URL} # - DB_NS=account-2 - - REGION_INFO=|Mongo;pg|Postgres - - TRANSACTOR_URL=ws://host.docker.internal:3333,ws://host.docker.internal:3331;;pg + - REGION_INFO=|Mongo;pg|Postgres;cockroach|CockroachDB + - TRANSACTOR_URL=ws://host.docker.internal:3333,ws://host.docker.internal:3331;;pg,ws://host.docker.internal:3332;;cockroach, - SES_URL= - STORAGE_CONFIG=${STORAGE_CONFIG} - FRONT_URL=http://host.docker.internal:8087 @@ -124,7 +132,6 @@ services: # - DB_URL=postgresql://postgres:example@postgres:5432 - SES_URL= - STORAGE_CONFIG=${STORAGE_CONFIG} - - FRONT_URL=http://host.docker.internal:8087 - RESERVED_DB_NAMES=telegram,gmail,github - MODEL_ENABLED=* - ACCOUNTS_URL=http://host.docker.internal:3000 @@ -160,6 +167,32 @@ services: - INIT_SCRIPT_URL=https://raw.githubusercontent.com/hcengineering/init/main/script.yaml # - INIT_WORKSPACE=onboarding restart: unless-stopped + workspace_cockroach: + image: hardcoreeng/workspace + extra_hosts: + - "host.docker.internal:host-gateway" + links: + - cockroach + - minio + - stats + volumes: + - ./branding.json:/var/cfg/branding.json + environment: + # - WS_OPERATION=create + - SERVER_SECRET=secret + - DB_URL=postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable + - STATS_URL=http://host.docker.internal:4900 + - SES_URL= + - REGION=cockroach + - STORAGE_CONFIG=${STORAGE_CONFIG} + - RESERVED_DB_NAMES=telegram,gmail,github + - MODEL_ENABLED=* + - ACCOUNTS_URL=http://host.docker.internal:3000 + - BRANDING_PATH=/var/cfg/branding.json + # - PARALLEL=2 + - INIT_SCRIPT_URL=https://raw.githubusercontent.com/hcengineering/init/main/script.yaml + # - INIT_WORKSPACE=onboarding + restart: unless-stopped collaborator: image: hardcoreeng/collaborator extra_hosts: @@ -270,15 +303,47 @@ services: environment: # - SERVER_PROVIDER=uweb # - UWS_HTTP_MAX_HEADERS_SIZE="32768" - - UV_THREADPOOL_SIZE=10 + # - UV_THREADPOOL_SIZE=10 - SERVER_PORT=3331 - SERVER_SECRET=secret - ENABLE_COMPRESSION=true - FULLTEXT_URL=http://host.docker.internal:4701 - STATS_URL=http://host.docker.internal:4900 - DB_URL=postgresql://postgres:example@postgres:5432 - - MONGO_URL=${MONGO_URL} - - 'MONGO_OPTIONS={"appName": "transactor-pg", "maxPoolSize": 1}' + - METRICS_CONSOLE=false + - METRICS_FILE=metrics.txt + - STORAGE_CONFIG=${STORAGE_CONFIG} + - FRONT_URL=http://host.docker.internal:8087 + # - APM_SERVER_URL=http://apm-server:8200 + - SES_URL='' + - ACCOUNTS_URL=http://host.docker.internal:3000 + - LAST_NAME_FIRST=true + - BRANDING_PATH=/var/cfg/branding.json + restart: unless-stopped + transactor_cockroach: + image: hardcoreeng/transactor + extra_hosts: + - "host.docker.internal:host-gateway" + links: + - cockroach + - minio + - account + - stats + # - apm-server + ports: + - 3332:3332 + volumes: + - ./branding.json:/var/cfg/branding.json + environment: + # - SERVER_PROVIDER=uweb + # - UWS_HTTP_MAX_HEADERS_SIZE="32768" + # - UV_THREADPOOL_SIZE=10 + - SERVER_PORT=3332 + - SERVER_SECRET=secret + - ENABLE_COMPRESSION=true + - FULLTEXT_URL=http://host.docker.internal:4702 + - STATS_URL=http://host.docker.internal:4900 + - DB_URL=postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable - METRICS_CONSOLE=false - METRICS_FILE=metrics.txt - STORAGE_CONFIG=${STORAGE_CONFIG} @@ -333,6 +398,26 @@ services: - STATS_URL=http://host.docker.internal:4900 - REKONI_URL=http://host.docker.internal:4004 - ACCOUNTS_URL=http://host.docker.internal:3000 + fulltext_cockroach: + image: hardcoreeng/fulltext + extra_hosts: + - "host.docker.internal:host-gateway" + restart: unless-stopped + links: + - elastic + - postgres + ports: + - 4702:4702 + environment: + - PORT=4702 + - SERVER_SECRET=secret + - DB_URL=postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable + - FULLTEXT_DB_URL=http://host.docker.internal:9200 + - ELASTIC_INDEX_NAME=local_storage_index + - STORAGE_CONFIG=${STORAGE_CONFIG} + - STATS_URL=http://host.docker.internal:4900 + - REKONI_URL=http://host.docker.internal:4004 + - ACCOUNTS_URL=http://host.docker.internal:3000 print: image: hardcoreeng/print extra_hosts: @@ -420,3 +505,4 @@ volumes: dbpg: files: elastic: + cockroach_db: From 5ea7ff26675e358c28cec58b3d0f06a74d824418 Mon Sep 17 00:00:00 2001 From: Artyom Savchenko Date: Thu, 14 Nov 2024 20:23:45 +0700 Subject: [PATCH 02/17] Fix test management header button (#7172) Signed-off-by: Artem Savchenko --- models/test-management/src/index.ts | 2 +- .../test-management-assets/assets/icons.svg | 3 + plugins/test-management-assets/src/index.ts | 2 +- .../TestManagementSpaceHeader.svelte | 132 ++++++++++++++++++ .../test-case/NewTestCaseHeader.svelte | 45 ------ .../test-management-resources/src/index.ts | 4 +- .../src/navigation.ts | 17 ++- .../test-management-resources/src/utils.ts | 10 ++ plugins/test-management/src/plugin.ts | 2 +- 9 files changed, 165 insertions(+), 52 deletions(-) create mode 100644 plugins/test-management-resources/src/components/TestManagementSpaceHeader.svelte delete mode 100644 plugins/test-management-resources/src/components/test-case/NewTestCaseHeader.svelte diff --git a/models/test-management/src/index.ts b/models/test-management/src/index.ts index 6b3a4dac7c..57cabf7eab 100644 --- a/models/test-management/src/index.ts +++ b/models/test-management/src/index.ts @@ -119,7 +119,7 @@ function defineApplication (builder: Builder): void { } ] }, - navHeaderComponent: testManagement.component.NewTestCaseHeader + navHeaderComponent: testManagement.component.TestManagementSpaceHeader }, testManagement.app.TestManagement ) diff --git a/plugins/test-management-assets/assets/icons.svg b/plugins/test-management-assets/assets/icons.svg index 961c3705a9..6fb417124a 100644 --- a/plugins/test-management-assets/assets/icons.svg +++ b/plugins/test-management-assets/assets/icons.svg @@ -49,4 +49,7 @@ + + + diff --git a/plugins/test-management-assets/src/index.ts b/plugins/test-management-assets/src/index.ts index 5498b02acc..8a50befa21 100644 --- a/plugins/test-management-assets/src/index.ts +++ b/plugins/test-management-assets/src/index.ts @@ -26,7 +26,7 @@ loadMetadata(testManagement.icon, { Home: `${icons}#home`, Estimation: `${icons}#testCase`, TestSuite: `${icons}#testSuite`, - TestProject: `${icons}#testCase`, + TestProject: `${icons}#project`, TestSuites: `${icons}#testSuite`, TestRuns: `${icons}#testRun`, RedCircle: `${icons}#red-circle`, diff --git a/plugins/test-management-resources/src/components/TestManagementSpaceHeader.svelte b/plugins/test-management-resources/src/components/TestManagementSpaceHeader.svelte new file mode 100644 index 0000000000..92ff76cdd5 --- /dev/null +++ b/plugins/test-management-resources/src/components/TestManagementSpaceHeader.svelte @@ -0,0 +1,132 @@ + + + +{#if loading} + +{:else} +
+ {#if hasProject} + { + void handleDropdownItemSelected(ev.detail) + }} + /> + {:else} +
+{/if} diff --git a/plugins/test-management-resources/src/components/test-case/NewTestCaseHeader.svelte b/plugins/test-management-resources/src/components/test-case/NewTestCaseHeader.svelte deleted file mode 100644 index 7730d978e3..0000000000 --- a/plugins/test-management-resources/src/components/test-case/NewTestCaseHeader.svelte +++ /dev/null @@ -1,45 +0,0 @@ - - - -
-
diff --git a/plugins/test-management-resources/src/index.ts b/plugins/test-management-resources/src/index.ts index 46a23aec10..3776daf5b4 100644 --- a/plugins/test-management-resources/src/index.ts +++ b/plugins/test-management-resources/src/index.ts @@ -14,7 +14,7 @@ // import { type Resources } from '@hcengineering/platform' -import NewTestCaseHeader from './components/test-case/NewTestCaseHeader.svelte' +import TestManagementSpaceHeader from './components/TestManagementSpaceHeader.svelte' import CreateProject from './components/project/CreateProject.svelte' import ProjectSpacePresenter from './components/project/ProjectSpacePresenter.svelte' import CreateTestSuite from './components/test-suite/CreateTestSuite.svelte' @@ -34,7 +34,7 @@ import { resolveLocation, getTestSuiteLink } from './navigation' export default async (): Promise => ({ component: { - NewTestCaseHeader, + TestManagementSpaceHeader, CreateProject, ProjectSpacePresenter, CreateTestSuite, diff --git a/plugins/test-management-resources/src/navigation.ts b/plugins/test-management-resources/src/navigation.ts index 9ffc30c818..775011ce07 100644 --- a/plugins/test-management-resources/src/navigation.ts +++ b/plugins/test-management-resources/src/navigation.ts @@ -14,10 +14,18 @@ import testManagement, { testManagementId, type TestSuite, type TestProject } from '@hcengineering/test-management' import { type Doc, type Ref } from '@hcengineering/core' import { getClient } from '@hcengineering/presentation' -import { getCurrentResolvedLocation, getPanelURI, type Location, type ResolvedLocation } from '@hcengineering/ui' +import { + getCurrentResolvedLocation, + getLocation, + getPanelURI, + type Location, + type ResolvedLocation +} from '@hcengineering/ui' import view, { type ObjectPanel } from '@hcengineering/view' import { accessDeniedStore } from '@hcengineering/view-resources' +const SUITE_KEY = 'attachedTo' + export function getPanelFragment (object: Pick): string { const hierarchy = getClient().getHierarchy() const objectPanelMixin = hierarchy.classHierarchyMixin(object._class, view.mixin.ObjectPanel) @@ -59,7 +67,7 @@ export function getTestSuiteLink (testSuite: Ref): Location { testSuite === undefined ? undefined : { - attachedTo: testSuite + [SUITE_KEY]: testSuite } return loc @@ -71,6 +79,11 @@ export function getTestSuiteIdFromFragment (fragment: string): Ref | return props[6] != null ? (props[6] as Ref) : undefined } +export function getTestSuiteIdFromLocation (): Ref { + const location = getLocation() + return (location?.query?.[SUITE_KEY] as Ref) ?? testManagement.ids.NoParent +} + export async function resolveLocation (loc: Location): Promise { if (loc.path[2] !== testManagementId) { return undefined diff --git a/plugins/test-management-resources/src/utils.ts b/plugins/test-management-resources/src/utils.ts index 8862576b0b..63fa18bb32 100644 --- a/plugins/test-management-resources/src/utils.ts +++ b/plugins/test-management-resources/src/utils.ts @@ -21,6 +21,8 @@ import { type TestProject, type TestCase, type TestSuite } from '@hcengineering/ import CreateTestSuiteComponent from './components/test-suite/CreateTestSuite.svelte' import EditTestSuiteComponent from './components/test-suite/EditTestSuite.svelte' +import CreateTestCase from './components/test-case/CreateTestCase.svelte' +import CreateProject from './components/project/CreateProject.svelte' export async function getPreviousAssignees (objectId: Ref | undefined): Promise>> { if (objectId === undefined) { @@ -62,6 +64,14 @@ export async function showEditTestSuitePopup (suite: Ref): Promise, testSuiteId: Ref): Promise { + showPopup(CreateTestCase, { space, testSuiteId }, 'top') +} + +export async function showCreateProjectPopup (): Promise { + showPopup(CreateProject, {}, 'top') +} + export async function CreateChildTestSuiteAction (doc: TestSuite): Promise { await showCreateTestSuitePopup(doc.space, doc._id) } diff --git a/plugins/test-management/src/plugin.ts b/plugins/test-management/src/plugin.ts index 6e7d69838c..e5104633ed 100644 --- a/plugins/test-management/src/plugin.ts +++ b/plugins/test-management/src/plugin.ts @@ -166,7 +166,7 @@ export const testManagementPlugin = plugin(testManagementId, { TestCaseSearchIcon: '' as AnyComponent, TestCases: '' as AnyComponent, CreateProject: '' as AnyComponent, - NewTestCaseHeader: '' as AnyComponent, + TestManagementSpaceHeader: '' as AnyComponent, TestCaseStatusIcon: '' as AnyComponent, PriorityIconPresenter: '' as AnyComponent, TestCaseStatusPresenter: '' as AnyComponent, From 63294b0c662e1d76086f0a738b4bddd18482f918 Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Thu, 14 Nov 2024 23:30:42 +0700 Subject: [PATCH 03/17] UBERF-8587: Fix github auth and delete issues (#7174) Signed-off-by: Andrey Sobolev --- packages/analytics-service/src/logging.ts | 6 +-- packages/core/src/measurements/context.ts | 19 +------ server/mongo/src/storage.ts | 18 ++++--- server/workspace-service/src/ws-operations.ts | 6 +-- services/github/pod-github/src/server.ts | 23 +++------ .../github/pod-github/src/sync/issueBase.ts | 7 +-- services/github/pod-github/src/sync/issues.ts | 7 +-- .../github/pod-github/src/sync/projects.ts | 48 +++++++----------- .../pod-github/src/sync/pullrequests.ts | 18 +++---- services/github/pod-github/src/worker.ts | 49 +++++++++++++------ 10 files changed, 90 insertions(+), 111 deletions(-) diff --git a/packages/analytics-service/src/logging.ts b/packages/analytics-service/src/logging.ts index 0288c690ed..e29185849b 100644 --- a/packages/analytics-service/src/logging.ts +++ b/packages/analytics-service/src/logging.ts @@ -7,8 +7,6 @@ import { basename, dirname, join } from 'path' import winston from 'winston' import DailyRotateFile from 'winston-daily-rotate-file' -const PLATFORM_OPERATION_LOGGING = process.env.PLATFORM_OPERATION_LOGGING === 'true' - export class SplitLogger implements MeasureLogger { logger: winston.Logger @@ -110,9 +108,7 @@ export class SplitLogger implements MeasureLogger { } logOperation (operation: string, time: number, params: ParamsType): void { - if (PLATFORM_OPERATION_LOGGING) { - this.logger.info({ operation, time, ...params }) - } + this.logger.info(operation, { time, ...params }) } childLogger (name: string, params: Record): MeasureLogger { diff --git a/packages/core/src/measurements/context.ts b/packages/core/src/measurements/context.ts index e5dadc7e23..a8d8dfa19f 100644 --- a/packages/core/src/measurements/context.ts +++ b/packages/core/src/measurements/context.ts @@ -64,22 +64,7 @@ export class MeasureMetricsContext implements MeasureContext { st = Date.now() contextData: object = {} private done (value?: number, override?: boolean): void { - updateMeasure( - this.metrics, - this.st, - this.params, - this.fullParams, - (spend) => { - this.logger.logOperation(this.name, spend, { - ...this.params, - ...(typeof this.fullParams === 'function' ? this.fullParams() : this.fullParams), - ...this.fullParams, - ...(this.logParams ?? {}) - }) - }, - value, - override - ) + updateMeasure(this.metrics, this.st, this.params, this.fullParams, (spend) => {}, value, override) } constructor ( @@ -144,7 +129,7 @@ export class MeasureMetricsContext implements MeasureContext { let needFinally = true try { const value = op(c) - if (value != null && value instanceof Promise) { + if (value instanceof Promise) { needFinally = false return value.finally(() => { c.end() diff --git a/server/mongo/src/storage.ts b/server/mongo/src/storage.ts index 1c3c47ec56..8fd5a576e4 100644 --- a/server/mongo/src/storage.ts +++ b/server/mongo/src/storage.ts @@ -653,7 +653,7 @@ abstract class MongoAdapterBase implements DbAdapter { try { result = await ctx.with( 'aggregate', - { clazz }, + {}, (ctx) => toArray(cursor), () => ({ domain, @@ -846,7 +846,7 @@ abstract class MongoAdapterBase implements DbAdapter { // Skip sort/projection/etc. return await ctx.with( 'find-one', - { domain }, + {}, async (ctx) => { const findOptions: MongoFindOptions = {} @@ -1029,7 +1029,8 @@ abstract class MongoAdapterBase implements DbAdapter { filter: { _id: it[0], '%hash%': null }, update: { $set: { '%hash%': it[1] } } } - })) + })), + { ordered: false } ) ) } @@ -1125,7 +1126,7 @@ abstract class MongoAdapterBase implements DbAdapter { } upload (ctx: MeasureContext, domain: Domain, docs: Doc[]): Promise { - return ctx.with('upload', { domain }, () => { + return ctx.with('upload', { domain }, (ctx) => { const coll = this.collection(domain) return uploadDocuments(ctx, docs, coll) @@ -1654,9 +1655,7 @@ export async function uploadDocuments (ctx: MeasureContext, docs: Doc[], coll: C if ('%hash%' in it) { delete it['%hash%'] } - const cs = ctx.newChild('calc-size', {}) - const size = calculateObjectSize(it) - cs.end() + const size = digest != null ? calculateObjectSize(it) : 0 return { replaceOne: { @@ -1665,7 +1664,10 @@ export async function uploadDocuments (ctx: MeasureContext, docs: Doc[], coll: C upsert: true } } - }) + }), + { + ordered: false + } ) } } diff --git a/server/workspace-service/src/ws-operations.ts b/server/workspace-service/src/ws-operations.ts index ce451bf8fb..43381755f0 100644 --- a/server/workspace-service/src/ws-operations.ts +++ b/server/workspace-service/src/ws-operations.ts @@ -132,9 +132,9 @@ export async function createWorkspace ( usePassedCtx: true }) const txAdapter = await txFactory(ctx, hierarchy, dbUrl, wsId, modelDb, storageAdapter) - await childLogger.withLog('init-workspace', {}, async (ctx) => { - await initModel(ctx, wsId, txes, txAdapter, storageAdapter, ctxModellogger, async (value) => {}) - }) + await childLogger.withLog('init-workspace', {}, (ctx) => + initModel(ctx, wsId, txes, txAdapter, storageAdapter, ctxModellogger, async (value) => {}) + ) const client = new TxOperations(wrapPipeline(ctx, pipeline, wsUrl), core.account.ConfigUser) diff --git a/services/github/pod-github/src/server.ts b/services/github/pod-github/src/server.ts index df41cd0a25..b8708c5275 100644 --- a/services/github/pod-github/src/server.ts +++ b/services/github/pod-github/src/server.ts @@ -86,18 +86,9 @@ export async function start (ctx: MeasureContext, brandingMap: BrandingMap): Pro body: req.body }) - ctx.info('map-installation', { - workspace: decodedToken.workspace.name, - installationid: payloadData.installationId - }) - await ctx.withLog('map-installation', {}, async (ctx) => { - await worker.mapInstallation( - ctx, - decodedToken.workspace.name, - payloadData.installationId, - payloadData.accountId - ) - }) + await ctx.with('map-installation', {}, (ctx) => + worker.mapInstallation(ctx, decodedToken.workspace.name, payloadData.installationId, payloadData.accountId) + ) res.status(200) res.json({}) } catch (err: any) { @@ -136,7 +127,7 @@ export async function start (ctx: MeasureContext, brandingMap: BrandingMap): Pro code: payloadData.code, state: payloadData.state }) - await ctx.withLog('request-github-access-token', {}, async (ctx) => { + await ctx.with('request-github-access-token', {}, async (ctx) => { await worker.requestGithubAccessToken({ workspace: decodedToken.workspace.name, accountId: payloadData.accountId, @@ -172,9 +163,9 @@ export async function start (ctx: MeasureContext, brandingMap: BrandingMap): Pro workspace: decodedToken.workspace.name, installationId: payloadData.installationId }) - await ctx.withLog('remove-installation', {}, async (ctx) => { - await worker.removeInstallation(ctx, decodedToken.workspace.name, payloadData.installationId) - }) + await ctx.with('remove-installation', {}, (ctx) => + worker.removeInstallation(ctx, decodedToken.workspace.name, payloadData.installationId) + ) res.status(200) res.json({}) } catch (err: any) { diff --git a/services/github/pod-github/src/sync/issueBase.ts b/services/github/pod-github/src/sync/issueBase.ts index 0b2365ce29..08ba502e24 100644 --- a/services/github/pod-github/src/sync/issueBase.ts +++ b/services/github/pod-github/src/sync/issueBase.ts @@ -734,8 +734,8 @@ export abstract class IssueSyncManagerBase { await this.ctx.withLog( 'create mixin issue', {}, - async () => - await this.client.createMixin( + () => + this.client.createMixin( existing._id as Ref, existing._class, existing.space, @@ -1259,7 +1259,8 @@ export abstract class IssueSyncManagerBase { if (!cnt) { Analytics.handleError(err) this.ctx.error('Error', { err }) - await derivedClient.update(info, { error: errorToObj(err) }) + await derivedClient.update(info, { error: errorToObj(err), needSync: githubSyncVersion }) + return false } } } diff --git a/services/github/pod-github/src/sync/issues.ts b/services/github/pod-github/src/sync/issues.ts index f4ccd33e7e..bc389a98a3 100644 --- a/services/github/pod-github/src/sync/issues.ts +++ b/services/github/pod-github/src/sync/issues.ts @@ -545,11 +545,8 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan url: issueExternal.url, workspace: this.provider.getWorkspaceId().name }) - target.prjData = await this.ctx.withLog( - 'add issue to project v2', - {}, - async () => - await this.addIssueToProject(container, okit, issueExternal, target.target.projectNodeId as string) + target.prjData = await this.ctx.withLog('add issue to project v2', {}, () => + this.addIssueToProject(container, okit, issueExternal, target.target.projectNodeId as string) ) if (target.prjData !== undefined) { issueExternal.projectItems.nodes.push(target.prjData) diff --git a/services/github/pod-github/src/sync/projects.ts b/services/github/pod-github/src/sync/projects.ts index 4748b1e41a..c6ca50ea9f 100644 --- a/services/github/pod-github/src/sync/projects.ts +++ b/services/github/pod-github/src/sync/projects.ts @@ -129,9 +129,7 @@ export class ProjectsSyncManager implements DocSyncManager { await this.ctx.withLog( 'Create Milestone projectV2', {}, - async () => { - await this.createMilestone(container.container, container.project, okit, milestone, info) - }, + () => this.createMilestone(container.container, container.project, okit, milestone, info), { label: milestone.label } ) } catch (err: any) { @@ -148,17 +146,9 @@ export class ProjectsSyncManager implements DocSyncManager { let { projectStructure, wasUpdates } = await this.ctx.withLog( 'update project structure', {}, - async () => - await syncRunner.exec( - m._id, - async () => - await this.updateFieldMappings( - container.container, - container.project, - m, - container.project.mixinClass, - okit - ) + () => + syncRunner.exec(m._id, () => + this.updateFieldMappings(container.container, container.project, m, container.project.mixinClass, okit) ), { label: milestone.label } ) @@ -168,7 +158,7 @@ export class ProjectsSyncManager implements DocSyncManager { projectStructure = (await this.ctx.withLog( 'update project structure(sync/second step)', {}, - async () => await this.queryProjectStructure(container.container, m), + () => this.queryProjectStructure(container.container, m), { label: m.label } @@ -340,7 +330,7 @@ export class ProjectsSyncManager implements DocSyncManager { const projectStructure = (await this.ctx.withLog( 'update project structure(handleEvent)', { prj: project.name }, - async () => await this.queryProjectStructure(integration, project) + () => this.queryProjectStructure(integration, project) )) as GithubProjectV2 integration.projectStructure.set(project._id, projectStructure) @@ -423,20 +413,18 @@ export class ProjectsSyncManager implements DocSyncManager { let { projectStructure, wasUpdates } = await this.ctx.withLog( 'update project structure', { prj: prj.name }, - async () => await this.updateFieldMappings(integration, prj, prj, prj.mixinClass, okit) + () => this.updateFieldMappings(integration, prj, prj, prj.mixinClass, okit) ) // Check if we have any changes in project, during our inactivity. - await this.ctx.withLog('check project v2 changes:', { prj: prj.name }, async () => { - await this.checkChanges(projectStructure, prj, prj._id, integration, derivedClient) - }) + await this.ctx.withLog('check project v2 changes:', { prj: prj.name }, () => + this.checkChanges(projectStructure, prj, prj._id, integration, derivedClient) + ) // Retrieve updated field if (wasUpdates) { - projectStructure = (await this.ctx.withLog( - 'update project structure(second pass)', - { prj: prj.name }, - async () => await this.queryProjectStructure(integration, prj) + projectStructure = (await this.ctx.withLog('update project structure(second pass)', { prj: prj.name }, () => + this.queryProjectStructure(integration, prj) )) as GithubProjectV2 } @@ -459,24 +447,24 @@ export class ProjectsSyncManager implements DocSyncManager { let { projectStructure, wasUpdates } = await this.ctx.withLog( 'update project structure', { prj: m.label }, - async () => - await syncRunner.exec( + () => + syncRunner.exec( m._id, async () => await this.updateFieldMappings(integration, prj, m, prj.mixinClass, okit) ) ) // Check if we have any changes in project, during our inactivity. - await this.ctx.withLog('check project v2 changes', { prj: prj.name }, async () => { - await this.checkChanges(projectStructure, m, prj._id, integration, derivedClient) - }) + await this.ctx.withLog('check project v2 changes', { prj: prj.name }, () => + this.checkChanges(projectStructure, m, prj._id, integration, derivedClient) + ) // Retrieve updated field if (wasUpdates) { projectStructure = (await this.ctx.withLog( 'update project structure(second pass)', { prj: prj.name }, - async () => await this.queryProjectStructure(integration, m) + () => this.queryProjectStructure(integration, m) )) as GithubProjectV2 } diff --git a/services/github/pod-github/src/sync/pullrequests.ts b/services/github/pod-github/src/sync/pullrequests.ts index 5978c48de0..be92156727 100644 --- a/services/github/pod-github/src/sync/pullrequests.ts +++ b/services/github/pod-github/src/sync/pullrequests.ts @@ -343,12 +343,12 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS private async createSyncData ( pullRequestExternal: PullRequestExternalData, - derivedClient: TxOperations | undefined, + derivedClient: TxOperations, repo: GithubIntegrationRepository, account: Ref ): Promise { const lastModified = new Date(pullRequestExternal.updatedAt).getTime() - await derivedClient?.createDoc(github.class.DocSyncInfo, repo.githubProject as Ref, { + await derivedClient.createDoc(github.class.DocSyncInfo, repo.githubProject as Ref, { url: pullRequestExternal.url.toLowerCase(), needSync: '', // we need to sync to retrieve patch in background githubNumber: pullRequestExternal.number, @@ -426,8 +426,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS target.prjData = await this.ctx.withLog( 'add pull request to project}', {}, - async () => - await this.addIssueToProject(container, okit, pullRequestExternal, target.target.projectNodeId as string), + () => this.addIssueToProject(container, okit, pullRequestExternal, target.target.projectNodeId as string), { url: pullRequestExternal.url } ) if (target.prjData !== undefined) { @@ -575,8 +574,8 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS await this.ctx.withLog( 'update pull request patch', {}, - async () => { - await this.handlePatch( + () => + this.handlePatch( info, container, pullRequestExternal, @@ -587,8 +586,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS }, lastModified, accountGH - ) - }, + ), { url: pullRequestExternal.url } ) } @@ -606,8 +604,8 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS const update = await this.ctx.withLog( 'perform pull request diff update', {}, - async () => - await this.handleDiffUpdate( + () => + this.handleDiffUpdate( target, { ...(existing as any), description }, info, diff --git a/services/github/pod-github/src/worker.ts b/services/github/pod-github/src/worker.ts index 69cfe74e67..fb797fe1ae 100644 --- a/services/github/pod-github/src/worker.ts +++ b/services/github/pod-github/src/worker.ts @@ -96,6 +96,8 @@ export class GithubWorker implements IntegrationManager { triggerRequests: number = 0 + authRequestSend = new Set>() + triggerSync: () => void = () => { this.triggerRequests++ } @@ -546,7 +548,7 @@ export class GithubWorker implements IntegrationManager { let record = await this.platform.getAccountByRef(this.workspace.name, account) // const accountRef = this.accounts.find((it) => it._id === account) - const accountRef = await this.liveQuery.findOne(contact.class.PersonAccount, { _id: account }) + const [accountRef] = await this.liveQuery.queryFind(contact.class.PersonAccount, { _id: account }) if (record === undefined) { if (accountRef !== undefined) { const accounts = this._client.getModel().getAccountByPersonId(accountRef.person) @@ -570,11 +572,22 @@ export class GithubWorker implements IntegrationManager { } // We need to inform user, he need to authorize this account with github. - if (accountRef !== undefined) { + if (accountRef !== undefined && !this.authRequestSend.has(accountRef._id)) { + this.authRequestSend.add(accountRef._id) const person = await this.liveQuery.findOne(contact.class.Person, { _id: accountRef.person }) if (person !== undefined) { const personSpace = await this.liveQuery.findOne(contact.class.PersonSpace, { person: person._id }) if (personSpace !== undefined) { + // We need to remove if user has authentication in workspace but doesn't have a record. + + const accounts = this._client.getModel().getAccountByPersonId(accountRef.person) + const authentications = await this.liveQuery.findAll(github.class.GithubAuthentication, { + createdBy: { $in: accounts.map((it) => it._id) } + }) + for (const auth of authentications) { + await this._client.remove(auth) + } + await createNotification(this._client, person, { user: account, space: personSpace._id, @@ -997,14 +1010,11 @@ export class GithubWorker implements IntegrationManager { async applyMigrations (): Promise { const key = 'lowerCaseDuplicates' // We need to apply migrations if required. - const migration = await this.client.findOne(core.class.MigrationState, { - plugin: githubId, - state: key - }) + const migrations = await this.client.findAll(core.class.MigrationState, {}) const derivedClient = new TxOperations(this.client, core.account.System, true) - if (migration === undefined) { + if (migrations.find((it) => it.plugin === githubId && it.state === key) === undefined) { let modifiedOn = 0 const limit = 1000 while (true) { @@ -1070,6 +1080,21 @@ export class GithubWorker implements IntegrationManager { state: key }) } + + const wrongAuthentications = 'migrate-wrong-authentications' + + if (migrations.find((it) => it.plugin === githubId && it.state === wrongAuthentications) === undefined) { + const auths = await this.client.findAll(github.class.GithubAuthentication, {}) + for (const auth of auths) { + if (auth.createdBy !== auth.modifiedBy) { + await this._client.remove(auth) + } + } + await derivedClient.createDoc(core.class.MigrationState, core.space.Configuration, { + plugin: githubId, + state: wrongAuthentications + }) + } } async syncAndWait (): Promise { @@ -1328,15 +1353,15 @@ export class GithubWorker implements IntegrationManager { if (info.deleted === true) { if (await mapper.handleDelete(existing, info, derivedClient, true)) { await derivedClient.remove(info) - continue } + continue } const docUpdate = await this.ctx.withLog( 'sync doc', {}, - async (ctx) => await mapper.sync(existing, info, parent, derivedClient), - { url: info.url.toLowerCase() } + (ctx) => mapper.sync(existing, info, parent, derivedClient), + { url: info.url.toLowerCase(), workspace: this.workspace.name } ) if (docUpdate !== undefined) { await derivedClient.update(info, docUpdate) @@ -1397,10 +1422,6 @@ export class GithubWorker implements IntegrationManager { }, 50) // Small timeout to aggregate few bulk changes. } } - // Wait up for every 60 seconds to refresh, just in case. - setTimeout(() => { - resolve() - }, 60 * 1000) }) } From 00cb05e25a49883a31847ed43b42dae7b768d407 Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Thu, 14 Nov 2024 23:30:57 +0700 Subject: [PATCH 04/17] Fix full text attribute error (#7173) Signed-off-by: Andrey Sobolev --- server-plugins/fulltext-resources/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-plugins/fulltext-resources/src/index.ts b/server-plugins/fulltext-resources/src/index.ts index b57617843d..e77064f87c 100644 --- a/server-plugins/fulltext-resources/src/index.ts +++ b/server-plugins/fulltext-resources/src/index.ts @@ -69,7 +69,7 @@ export async function OnChange (txes: Tx[], control: TriggerControl): Promise Date: Thu, 14 Nov 2024 22:57:53 +0500 Subject: [PATCH 05/17] Fix pg nested query (#7175) Signed-off-by: Denis Bykhov --- server/postgres/src/storage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/postgres/src/storage.ts b/server/postgres/src/storage.ts index 84c63c2118..ba94039d2b 100644 --- a/server/postgres/src/storage.ts +++ b/server/postgres/src/storage.ts @@ -944,7 +944,7 @@ abstract class PostgresAdapterBase implements DbAdapter { } else { tKey += arr[i] if (i !== arr.length - 1) { - tKey += '.' + tKey += ',' } } // Check if key is belong to mixin class, we need to add prefix. From 8bfeba7303ab93245605b0fd099b58c547601e6b Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Fri, 15 Nov 2024 09:49:35 +0500 Subject: [PATCH 06/17] Remove collection tx (#7171) Signed-off-by: Denis Bykhov --- dev/tool/src/clean.ts | 43 ++--- dev/tool/src/mixin.ts | 12 +- models/core/src/index.ts | 28 +-- models/core/src/migration.ts | 36 ++++ models/core/src/tx.ts | 8 - models/server-activity/src/index.ts | 10 +- models/server-activity/src/migration.ts | 24 +-- models/server-attachment/src/index.ts | 5 +- models/server-calendar/src/index.ts | 2 +- models/server-chunter/src/index.ts | 10 +- models/server-collaboration/src/index.ts | 5 +- .../server-controlled-documents/src/index.ts | 9 +- models/server-gmail/src/index.ts | 5 +- models/server-hr/src/index.ts | 20 +- models/server-notification/src/index.ts | 5 +- models/server-request/src/index.ts | 5 +- models/server-setting/src/index.ts | 7 +- models/server-tags/src/index.ts | 6 +- models/server-task/src/index.ts | 6 +- models/server-telegram/src/index.ts | 5 +- models/server-time/src/index.ts | 25 +-- models/task/src/migration.ts | 43 ----- packages/core/src/__tests__/minmodel.ts | 7 - packages/core/src/client.ts | 9 +- packages/core/src/component.ts | 2 - packages/core/src/memdb.ts | 23 +-- packages/core/src/operations.ts | 6 +- packages/core/src/tx.ts | 94 +++------- packages/presentation/src/utils.ts | 4 +- packages/query/src/__tests__/minmodel.ts | 7 - packages/query/src/__tests__/query.test.ts | 43 +++-- packages/query/src/index.ts | 39 ---- .../src/components/editor/CardActions.svelte | 2 +- .../ReverseChannelScrollView.svelte | 10 +- .../src/components/Dashboard.svelte | 7 +- .../test-management-resources/src/utils.ts | 17 +- .../team/calendar/TeamCalendar.svelte | 4 +- .../components/issues/AssigneeEditor.svelte | 2 +- .../issues/IssueStatusActivity.svelte | 18 +- plugins/tracker-resources/src/utils.ts | 18 +- plugins/view-resources/src/middleware.ts | 3 +- plugins/view-resources/src/utils.ts | 19 +- .../activity-resources/src/index.ts | 173 ++++++++---------- .../activity-resources/src/references.ts | 85 +++++---- .../activity-resources/src/utils.ts | 25 +-- server-plugins/activity/src/utils.ts | 65 ++----- server-plugins/ai-bot-resources/src/index.ts | 24 +-- .../attachment-resources/src/index.ts | 3 +- .../calendar-resources/src/index.ts | 4 +- server-plugins/chunter-resources/src/index.ts | 54 +++--- .../collaboration-resources/src/index.ts | 6 +- .../src/index.ts | 10 +- server-plugins/drive-resources/src/index.ts | 5 +- .../fulltext-resources/src/index.ts | 8 +- server-plugins/gmail-resources/src/index.ts | 2 +- server-plugins/guest-resources/src/index.ts | 4 +- server-plugins/hr-resources/src/index.ts | 19 +- server-plugins/love-resources/src/index.ts | 10 +- .../notification-resources/src/index.ts | 110 +++++------ .../notification-resources/src/utils.ts | 40 ++-- server-plugins/recruit-resources/src/index.ts | 2 +- server-plugins/request-resources/src/index.ts | 40 ++-- server-plugins/setting-resources/src/index.ts | 6 +- server-plugins/tags-resources/src/index.ts | 12 +- server-plugins/task-resources/src/index.ts | 9 +- .../telegram-resources/src/index.ts | 2 +- server-plugins/time-resources/src/index.ts | 24 ++- server-plugins/tracker-resources/src/index.ts | 143 +++++++-------- server/backup/src/backup.ts | 34 +--- server/core/src/triggers.ts | 2 +- server/middleware/src/broadcast.ts | 8 +- server/middleware/src/domainTx.ts | 2 +- server/middleware/src/modified.ts | 5 +- server/middleware/src/spacePermissions.ts | 32 +--- server/middleware/src/spaceSecurity.ts | 3 +- server/middleware/src/triggers.ts | 49 +++-- server/mongo/src/__tests__/minmodel.ts | 7 - server/mongo/src/storage.ts | 25 --- server/postgres/src/__tests__/minmodel.ts | 7 - server/postgres/src/storage.ts | 32 +--- server/server/src/client.ts | 8 +- server/ws/src/__tests__/minmodel.ts | 7 - .../ai-bot/pod-ai-bot/src/workspace/love.ts | 37 ++-- .../src/workspace/supportWsClient.ts | 7 +- .../src/workspace/workspaceClient.ts | 22 +-- .../src/supportWsClient.ts | 3 +- .../pod-calendar/src/workspaceClient.ts | 30 ++- .../pod-github/src/sync/pullrequests.ts | 17 +- .../server-github-resources/src/index.ts | 24 +-- services/gmail/pod-gmail/src/gmail.ts | 20 +- .../gmail/pod-gmail/src/workspaceClient.ts | 10 - .../telegram/pod-telegram/src/workspace.ts | 82 ++++----- 92 files changed, 749 insertions(+), 1262 deletions(-) diff --git a/dev/tool/src/clean.ts b/dev/tool/src/clean.ts index e3022a3d1a..33ad27850e 100644 --- a/dev/tool/src/clean.ts +++ b/dev/tool/src/clean.ts @@ -20,7 +20,6 @@ import { loadCollaborativeDoc, saveCollaborativeDoc, yDocToBuffer } from '@hceng import contact from '@hcengineering/contact' import core, { type ArrOf, - type AttachedDoc, type BackupClient, type Class, ClassifierKind, @@ -47,7 +46,6 @@ import core, { type StatusCategory, type Tx, type TxCUD, - type TxCollectionCUD, type TxCreateDoc, type TxMixin, TxOperations, @@ -158,16 +156,6 @@ export async function cleanWorkspace ( await db.collection(DOMAIN_TX).deleteMany({ objectId: (tx as TxRemoveDoc).objectId }) processed++ } - if ( - tx._class === core.class.TxCollectionCUD && - (tx as TxCollectionCUD).tx._class === core.class.TxRemoveDoc - ) { - // We need to remove all update and create operations for document - await db.collection(DOMAIN_TX).deleteMany({ - 'tx.objectId': ((tx as TxCollectionCUD).tx as TxRemoveDoc).objectId - }) - processed++ - } } if (processed % 1000 === 0) { console.log('processed', processed) @@ -219,18 +207,13 @@ export async function cleanRemovedTransactions (workspaceId: WorkspaceId, transa try { let count = 0 while (true) { - const removedDocs = await connection.findAll( - core.class.TxCollectionCUD, - { 'tx._class': core.class.TxRemoveDoc }, - { limit: 1000 } - ) + const removedDocs = await connection.findAll(core.class.TxRemoveDoc, {}, { limit: 1000 }) if (removedDocs.length === 0) { break } - const toRemove = await connection.findAll(core.class.TxCollectionCUD, { - 'tx._class': { $in: [core.class.TxCreateDoc, core.class.TxRemoveDoc, core.class.TxUpdateDoc] }, - 'tx.objectId': { $in: removedDocs.map((it) => it.tx.objectId) } + const toRemove = await connection.findAll(core.class.TxCUD, { + objectId: { $in: removedDocs.map((it) => it.objectId) } }) await connection.clean( DOMAIN_TX, @@ -368,20 +351,18 @@ export async function fixCommentDoubleIdCreate (workspaceId: WorkspaceId, transa mode: 'backup' })) as unknown as CoreClient & BackupClient try { - const commentTxes = await connection.findAll(core.class.TxCollectionCUD, { - 'tx._class': core.class.TxCreateDoc, - 'tx.objectClass': chunter.class.ChatMessage + const commentTxes = await connection.findAll(core.class.TxCreateDoc, { + objectClass: chunter.class.ChatMessage }) - const commentTxesRemoved = await connection.findAll(core.class.TxCollectionCUD, { - 'tx._class': core.class.TxRemoveDoc, - 'tx.objectClass': chunter.class.ChatMessage + const commentTxesRemoved = await connection.findAll(core.class.TxRemoveDoc, { + objectClass: chunter.class.ChatMessage }) - const removed = new Map(commentTxesRemoved.map((it) => [it.tx.objectId, it])) + const removed = new Map(commentTxesRemoved.map((it) => [it.objectId, it])) // Do not checked removed const objSet = new Set>() const oldValue = new Map, string>() for (const c of commentTxes) { - const cid = c.tx.objectId + const cid = c.objectId if (removed.has(cid)) { continue } @@ -389,7 +370,7 @@ export async function fixCommentDoubleIdCreate (workspaceId: WorkspaceId, transa objSet.add(cid) if (has) { // We have found duplicate one, let's rename it. - const doc = TxProcessor.createDoc2Doc(c.tx as unknown as TxCreateDoc) + const doc = TxProcessor.createDoc2Doc(c as unknown as TxCreateDoc) if (doc.message !== '' && doc.message.trim() !== '

') { await connection.clean(DOMAIN_TX, [c._id]) if (oldValue.get(cid) === doc.message.trim()) { @@ -398,8 +379,8 @@ export async function fixCommentDoubleIdCreate (workspaceId: WorkspaceId, transa oldValue.set(doc._id, doc.message) console.log('renaming', cid, doc.message) // Remove previous transaction. - c.tx.objectId = generateId() - doc._id = c.tx.objectId as Ref + c.objectId = generateId() + doc._id = c.objectId as Ref await connection.upload(DOMAIN_TX, [c]) // Also we need to create snapsot await connection.upload(DOMAIN_ACTIVITY, [doc]) diff --git a/dev/tool/src/mixin.ts b/dev/tool/src/mixin.ts index d967b9a974..1fb66f142c 100644 --- a/dev/tool/src/mixin.ts +++ b/dev/tool/src/mixin.ts @@ -215,17 +215,7 @@ async function getMixinWithForeignProperties ( { limit: 1, sort: { modifiedOn: SortingOrder.Descending } } ) - const collectionTx = await connection.findAll( - core.class.TxCollectionCUD, - { - 'tx._class': core.class.TxUpdateDoc, - 'tx.objectId': doc._id, - [`tx.operations.${property}`]: { $exists: true } - }, - { limit: 1, sort: { modifiedOn: SortingOrder.Descending } } - ) - - const cModifiedOn = Math.max(updateDocTx[0]?.modifiedOn ?? 0, collectionTx[0]?.modifiedOn ?? 0) + const cModifiedOn = updateDocTx[0]?.modifiedOn ?? 0 const mModifiedOn = mixinTx[0]?.modifiedOn ?? 0 properties.push({ name: property, cValue, mValue, cModifiedOn, mModifiedOn }) diff --git a/models/core/src/index.ts b/models/core/src/index.ts index ef1b6c943a..26445f9f9d 100644 --- a/models/core/src/index.ts +++ b/models/core/src/index.ts @@ -24,12 +24,7 @@ import { DOMAIN_STATUS, DOMAIN_TRANSIENT, DOMAIN_TX, - systemAccountEmail, - type AttachedDoc, - type Class, - type Doc, - type IndexingConfiguration, - type TxCollectionCUD + systemAccountEmail } from '@hcengineering/core' import { type Builder } from '@hcengineering/model' import { TBenchmarkDoc } from './benchmark' @@ -91,17 +86,7 @@ import { import { defineSpaceType } from './spaceType' import { TDomainStatusPlaceholder, TStatus, TStatusCategory } from './status' import { TUserStatus } from './transient' -import { - TTx, - TTxApplyIf, - TTxCollectionCUD, - TTxCreateDoc, - TTxCUD, - TTxMixin, - TTxRemoveDoc, - TTxUpdateDoc, - TTxWorkspaceEvent -} from './tx' +import { TTx, TTxApplyIf, TTxCreateDoc, TTxCUD, TTxMixin, TTxRemoveDoc, TTxUpdateDoc, TTxWorkspaceEvent } from './tx' export { coreId, DOMAIN_SPACE } from '@hcengineering/core' export * from './core' @@ -122,7 +107,6 @@ export function createModel (builder: Builder): void { TTxCUD, TTxCreateDoc, TAttachedDoc, - TTxCollectionCUD, TTxMixin, TTxUpdateDoc, TTxRemoveDoc, @@ -188,14 +172,6 @@ export function createModel (builder: Builder): void { core.account.System ) - builder.mixin>, IndexingConfiguration>>( - core.class.TxCollectionCUD, - core.class.Class, - core.mixin.IndexConfiguration, - { - indexes: ['tx.objectId', 'tx.operations.attachedTo'] - } - ) builder.createDoc(core.class.DomainIndexConfiguration, core.space.Model, { domain: DOMAIN_TX, disabled: [ diff --git a/models/core/src/migration.ts b/models/core/src/migration.ts index a692f66ab1..815481049e 100644 --- a/models/core/src/migration.ts +++ b/models/core/src/migration.ts @@ -15,11 +15,14 @@ import { saveCollaborativeDoc } from '@hcengineering/collaboration' import core, { + type Class, DOMAIN_SPACE, DOMAIN_STATUS, DOMAIN_TX, MeasureMetricsContext, RateLimiter, + type Ref, + type TxCUD, collaborativeDocParse, coreId, generateId, @@ -285,6 +288,39 @@ export const coreOperation: MigrateOperation = { await client.update(DOMAIN_TX, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } }) await client.update(DOMAIN_SPACE, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } }) } + }, + { + state: 'remove-collection-txes', + func: async (client) => { + let processed = 0 + while (true) { + const txes = await client.find>( + DOMAIN_TX, + { + _class: 'core:class:TxCollectionCUD' as Ref> + }, + { limit: 5000 } + ) + if (txes.length === 0) break + for (const tx of txes) { + processed++ + await client.update( + DOMAIN_TX, + { _id: tx._id }, + { + $set: { + attachedTo: tx.objectId, + attachedToClass: tx.objectClass, + ...(tx as any).tx + } + } + ) + if (processed % 1000 === 0) { + console.log('processed', processed) + } + } + } + } } ]) }, diff --git a/models/core/src/tx.ts b/models/core/src/tx.ts index c12cfd1e09..f688761ecf 100644 --- a/models/core/src/tx.ts +++ b/models/core/src/tx.ts @@ -14,7 +14,6 @@ // import { - type AttachedDoc, type Class, type Data, type Doc, @@ -28,7 +27,6 @@ import { type Space, type Tx, type TxApplyIf, - type TxCollectionCUD, type TxCreateDoc, type TxCUD, type TxMixin, @@ -72,12 +70,6 @@ export class TTxCreateDoc extends TTxCUD implements TxCreateDo attributes!: Data } -@Model(core.class.TxCollectionCUD, core.class.TxCUD) -export class TTxCollectionCUD extends TTxCUD implements TxCollectionCUD { - collection!: string - tx!: TxCUD

-} - @Model(core.class.TxMixin, core.class.TxCUD) export class TTxMixin extends TTxCUD implements TxMixin { mixin!: Ref> diff --git a/models/server-activity/src/index.ts b/models/server-activity/src/index.ts index 5d4820a247..c48dec6f71 100644 --- a/models/server-activity/src/index.ts +++ b/models/server-activity/src/index.ts @@ -36,14 +36,16 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverActivity.trigger.OnReactionChanged, txMatch: { - collection: 'reactions', - _class: core.class.TxCollectionCUD + collection: 'reactions' }, isAsync: true }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverActivity.trigger.ActivityMessagesHandler, + txMatch: { + objectClass: { $nin: [activity.class.ActivityMessage, notification.class.DocNotifyContext] } + }, isAsync: true }) @@ -54,8 +56,8 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverActivity.trigger.ReferenceTrigger, txMatch: { - 'tx.objectClass': { $ne: activity.class.ActivityReference }, - objectClass: { + objectClass: { $ne: activity.class.ActivityReference }, + attachedToClass: { $nin: [ notification.class.InboxNotification, notification.class.DocNotifyContext, diff --git a/models/server-activity/src/migration.ts b/models/server-activity/src/migration.ts index f570bde1c9..24f1b44ac4 100644 --- a/models/server-activity/src/migration.ts +++ b/models/server-activity/src/migration.ts @@ -18,16 +18,13 @@ import core, { MeasureMetricsContext, SortingOrder, TxFactory, - TxProcessor, toFindResult, toIdMap, - type AttachedDoc, type Class, type Doc, type Ref, type Tx, type TxCUD, - type TxCollectionCUD, type TxCreateDoc } from '@hcengineering/core' import { @@ -85,12 +82,11 @@ async function generateDocUpdateMessageByTx ( tx, control, undefined, - undefined, objectCache ) for (const collectionTx of createCollectionCUDTxes) { - const createTx = collectionTx.tx as TxCreateDoc + const createTx = collectionTx as TxCreateDoc const domain = client.hierarchy.getDomain(createTx.objectClass) await client.create(domain, { @@ -152,15 +148,14 @@ async function createDocUpdateMessages (client: MigrationClient): Promise continue } - if (v._class === core.class.TxCollectionCUD) { + if (v.attachedToClass !== undefined && v.attachedTo !== undefined) { try { - const vcol = v as TxCollectionCUD - const _cl = client.hierarchy.getBaseClass(vcol.tx.objectClass) + const _cl = client.hierarchy.getBaseClass(v.attachedToClass) const s = byClass.get(_cl) ?? new Set() - s.add(vcol.tx.objectId) + s.add(v.attachedTo) byClass.set(_cl, s) } catch { - const objClass = (v as TxCollectionCUD).tx.objectClass + const objClass = v.attachedToClass const has = classNotFound.has(objClass) if (!has) { classNotFound.add(objClass) @@ -198,8 +193,7 @@ async function createDocUpdateMessages (client: MigrationClient): Promise } const transactions = allTransactions.get(d._id) ?? [] for (const tx of transactions) { - const innerTx = TxProcessor.extractTx(tx) as TxCUD - txIds.add(innerTx._id) + txIds.add(tx._id) } } @@ -220,10 +214,8 @@ async function createDocUpdateMessages (client: MigrationClient): Promise } const transactions = allTransactions.get(d._id) ?? [] for (const tx of transactions) { - const innerTx = TxProcessor.extractTx(tx) as TxCUD - - if (!client.hierarchy.hasClass(innerTx.objectClass)) { - const objClass = innerTx.objectClass + if (!client.hierarchy.hasClass(tx.objectClass)) { + const objClass = tx.objectClass const has = classNotFound.has(objClass) if (!has) { classNotFound.add(objClass) diff --git a/models/server-attachment/src/index.ts b/models/server-attachment/src/index.ts index ffabc76d7e..699e983fad 100644 --- a/models/server-attachment/src/index.ts +++ b/models/server-attachment/src/index.ts @@ -27,9 +27,8 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverAttachment.trigger.OnAttachmentDelete, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': attachment.class.Attachment, - 'tx._class': core.class.TxRemoveDoc + _class: core.class.TxRemoveDoc, + objectClass: attachment.class.Attachment } }) } diff --git a/models/server-calendar/src/index.ts b/models/server-calendar/src/index.ts index 13d285e317..06567046db 100644 --- a/models/server-calendar/src/index.ts +++ b/models/server-calendar/src/index.ts @@ -44,7 +44,7 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverCalendar.trigger.OnEvent, txMatch: { - 'tx.objectClass': calendar.class.Event + objectClass: calendar.class.Event } }) diff --git a/models/server-chunter/src/index.ts b/models/server-chunter/src/index.ts index 5887285e75..4f63c95de9 100644 --- a/models/server-chunter/src/index.ts +++ b/models/server-chunter/src/index.ts @@ -78,18 +78,16 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverChunter.trigger.OnChatMessageRemoved, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx._class': core.class.TxRemoveDoc, - 'tx.objectClass': chunter.class.ChatMessage + _class: core.class.TxRemoveDoc, + objectClass: chunter.class.ChatMessage } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverChunter.trigger.ChatNotificationsHandler, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx._class': core.class.TxCreateDoc, - 'tx.objectClass': chunter.class.ChatMessage + _class: core.class.TxCreateDoc, + objectClass: chunter.class.ChatMessage }, isAsync: true }) diff --git a/models/server-collaboration/src/index.ts b/models/server-collaboration/src/index.ts index 30e6796737..bbccadf756 100644 --- a/models/server-collaboration/src/index.ts +++ b/models/server-collaboration/src/index.ts @@ -23,6 +23,9 @@ export { serverCollaborationId } from '@hcengineering/server-collaboration' export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverCollaboration.trigger.OnDelete + trigger: serverCollaboration.trigger.OnDelete, + txMatch: { + _class: core.class.TxRemoveDoc + } }) } diff --git a/models/server-controlled-documents/src/index.ts b/models/server-controlled-documents/src/index.ts index 1bd4370205..535e3a0b4f 100644 --- a/models/server-controlled-documents/src/index.ts +++ b/models/server-controlled-documents/src/index.ts @@ -33,11 +33,10 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverDocuments.trigger.OnDocApprovalRequestApproved, txMatch: { - _class: core.class.TxCollectionCUD, - objectClass: documents.class.ControlledDocument, - 'tx._class': core.class.TxUpdateDoc, - 'tx.objectClass': documents.class.DocumentApprovalRequest, - 'tx.operations.status': RequestStatus.Completed + _class: core.class.TxUpdateDoc, + attachedToClass: documents.class.ControlledDocument, + objectClass: documents.class.DocumentApprovalRequest, + 'operations.status': RequestStatus.Completed } }) diff --git a/models/server-gmail/src/index.ts b/models/server-gmail/src/index.ts index 3715b8f060..d1837e989c 100644 --- a/models/server-gmail/src/index.ts +++ b/models/server-gmail/src/index.ts @@ -42,9 +42,8 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverGmail.trigger.OnMessageCreate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': gmail.class.Message, - 'tx._class': core.class.TxCreateDoc + _class: core.class.TxCreateDoc, + objectClass: gmail.class.Message } }) diff --git a/models/server-hr/src/index.ts b/models/server-hr/src/index.ts index da31dc0808..5370433f12 100644 --- a/models/server-hr/src/index.ts +++ b/models/server-hr/src/index.ts @@ -44,27 +44,24 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverHr.trigger.OnRequestCreate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': hr.class.Request, - 'tx._class': core.class.TxCreateDoc + _class: core.class.TxCreateDoc, + objectClass: hr.class.Request } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverHr.trigger.OnRequestUpdate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': hr.class.Request, - 'tx._class': core.class.TxUpdateDoc + _class: core.class.TxUpdateDoc, + objectClass: hr.class.Request } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverHr.trigger.OnRequestRemove, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': hr.class.Request, - 'tx._class': core.class.TxRemoveDoc + _class: core.class.TxRemoveDoc, + objectClass: hr.class.Request } }) @@ -88,9 +85,8 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverHr.trigger.OnPublicHolidayCreate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': hr.class.PublicHoliday, - 'tx._class': core.class.TxCreateDoc + _class: core.class.TxCreateDoc, + objectClass: hr.class.PublicHoliday } }) diff --git a/models/server-notification/src/index.ts b/models/server-notification/src/index.ts index 7a84045db4..02d29ebba8 100644 --- a/models/server-notification/src/index.ts +++ b/models/server-notification/src/index.ts @@ -89,7 +89,10 @@ export function createModel (builder: Builder): void { }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverNotification.trigger.OnDocRemove + trigger: serverNotification.trigger.OnDocRemove, + txMatch: { + _class: core.class.TxRemoveDoc + } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { diff --git a/models/server-request/src/index.ts b/models/server-request/src/index.ts index 007cceb17d..57bf3580f8 100644 --- a/models/server-request/src/index.ts +++ b/models/server-request/src/index.ts @@ -25,7 +25,10 @@ export { serverRequestId } from '@hcengineering/server-request' export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverRequest.trigger.OnRequest + trigger: serverRequest.trigger.OnRequest, + txMatch: { + objectClass: request.class.Request + } }) builder.mixin(request.class.Request, core.class.Class, serverNotification.mixin.TextPresenter, { diff --git a/models/server-setting/src/index.ts b/models/server-setting/src/index.ts index 8f0b5b3a34..403b10f9e8 100644 --- a/models/server-setting/src/index.ts +++ b/models/server-setting/src/index.ts @@ -68,11 +68,8 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverSetting.trigger.OnRoleNameUpdate, txMatch: { - _class: core.class.TxCollectionCUD, - objectSpace: core.space.Model, - collection: 'roles', - 'tx._class': core.class.TxUpdateDoc, - 'tx.objectClass': core.class.Role + _class: core.class.TxUpdateDoc, + objectClass: core.class.Role } }) } diff --git a/models/server-tags/src/index.ts b/models/server-tags/src/index.ts index 842b1d05d7..3d55219b8f 100644 --- a/models/server-tags/src/index.ts +++ b/models/server-tags/src/index.ts @@ -23,7 +23,11 @@ export { serverTagsId } from '@hcengineering/server-tags' export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverTags.trigger.onTagReference + trigger: serverTags.trigger.onTagReference, + txMatch: { + _class: { $in: [core.class.TxCreateDoc, core.class.TxRemoveDoc] }, + objectClass: tags.class.TagReference + } }) builder.mixin, ObjectDDParticipant>( diff --git a/models/server-task/src/index.ts b/models/server-task/src/index.ts index 80e2227a88..bda31cf670 100644 --- a/models/server-task/src/index.ts +++ b/models/server-task/src/index.ts @@ -15,6 +15,7 @@ import core from '@hcengineering/core/src/component' import { type Builder } from '@hcengineering/model' +import task from '@hcengineering/task' import serverCore from '@hcengineering/server-core' import serverTask from '@hcengineering/server-task' @@ -22,6 +23,9 @@ export { serverTaskId } from '@hcengineering/server-task' export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverTask.trigger.OnStateUpdate + trigger: serverTask.trigger.OnStateUpdate, + txMatch: { + objectClass: task.class.Task + } }) } diff --git a/models/server-telegram/src/index.ts b/models/server-telegram/src/index.ts index 07b58aa547..7ea53af712 100644 --- a/models/server-telegram/src/index.ts +++ b/models/server-telegram/src/index.ts @@ -45,9 +45,8 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTelegram.trigger.OnMessageCreate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': telegram.class.Message, - 'tx._class': core.class.TxCreateDoc + _class: core.class.TxCreateDoc, + objectClass: telegram.class.Message } }) diff --git a/models/server-time/src/index.ts b/models/server-time/src/index.ts index 152eacf9f0..04298a2009 100644 --- a/models/server-time/src/index.ts +++ b/models/server-time/src/index.ts @@ -44,27 +44,24 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTime.trigger.OnToDoUpdate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': time.class.ToDo, - 'tx._class': core.class.TxUpdateDoc + _class: core.class.TxUpdateDoc, + objectClass: time.class.ToDo } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTime.trigger.OnToDoRemove, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': time.class.ToDo, - 'tx._class': core.class.TxRemoveDoc + _class: core.class.TxRemoveDoc, + objectClass: time.class.ToDo } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTime.trigger.OnToDoCreate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': time.class.ToDo, - 'tx._class': core.class.TxCreateDoc + _class: core.class.TxCreateDoc, + objectClass: time.class.ToDo }, isAsync: true }) @@ -72,18 +69,16 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTime.trigger.OnWorkSlotCreate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': time.class.WorkSlot, - 'tx._class': core.class.TxCreateDoc + _class: core.class.TxCreateDoc, + objectClass: time.class.WorkSlot } }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTime.trigger.OnWorkSlotUpdate, txMatch: { - _class: core.class.TxCollectionCUD, - 'tx.objectClass': time.class.WorkSlot, - 'tx._class': core.class.TxUpdateDoc + _class: core.class.TxUpdateDoc, + objectClass: time.class.WorkSlot } }) diff --git a/models/task/src/migration.ts b/models/task/src/migration.ts index 9d0f50ac8f..47a20ea420 100644 --- a/models/task/src/migration.ts +++ b/models/task/src/migration.ts @@ -25,7 +25,6 @@ import { type Ref, type Space, type Status, - type TxCollectionCUD, type TxCreateDoc, type TxUpdateDoc } from '@hcengineering/core' @@ -447,48 +446,6 @@ export async function migrateDefaultStatusesBase ( } logger.log('affectedBaseTasks updated: ', counter) - const baseTaskCreateTxes = await client.find>(DOMAIN_TX, { - _class: core.class.TxCollectionCUD, - 'tx._class': core.class.TxCreateDoc, - 'tx.objectClass': { $in: baseTaskClasses }, - 'tx.attributes.status': { $in: statusIdsBeingMigrated } - }) - - logger.log('Base task create TXes: ', baseTaskCreateTxes.length) - - counter = 0 - for (const baseTaskCreateTx of baseTaskCreateTxes) { - const tx = baseTaskCreateTx.tx as TxCreateDoc - const newStatus = getNewStatus(tx.attributes.status) - - if (newStatus !== tx.attributes.status) { - counter++ - await client.update(DOMAIN_TX, { _id: baseTaskCreateTx._id }, { $set: { 'tx.attributes.status': newStatus } }) - } - } - logger.log('Base task create TXes updated: ', counter) - - const baseTaskUpdateTxes = await client.find>(DOMAIN_TX, { - _class: core.class.TxCollectionCUD, - 'tx._class': core.class.TxUpdateDoc, - 'tx.objectClass': { $in: baseTaskClasses }, - 'tx.operations.status': { $in: statusIdsBeingMigrated } - }) - - logger.log('Base task update TXes: ', baseTaskUpdateTxes.length) - - counter = 0 - for (const baseTaskUpdateTx of baseTaskUpdateTxes) { - const tx = baseTaskUpdateTx.tx as TxUpdateDoc - const newStatus = tx.operations.status !== undefined ? getNewStatus(tx.operations.status) : undefined - - if (newStatus !== tx.operations.status) { - counter++ - await client.update(DOMAIN_TX, { _id: baseTaskUpdateTx._id }, { $set: { 'tx.operations.status': newStatus } }) - } - } - logger.log('Base task update TXes updated: ', counter) - const baseTaskUpdateMessages = await client.find(DOMAIN_ACTIVITY, { _class: activity.class.DocUpdateMessage, action: 'update', diff --git a/packages/core/src/__tests__/minmodel.ts b/packages/core/src/__tests__/minmodel.ts index 33f3c04a5f..134fc778fd 100644 --- a/packages/core/src/__tests__/minmodel.ts +++ b/packages/core/src/__tests__/minmodel.ts @@ -189,13 +189,6 @@ export function genMinModel (): TxCUD[] { kind: ClassifierKind.CLASS }) ) - txes.push( - createClass(core.class.TxCollectionCUD, { - label: 'TxCollectionCUD' as IntlString, - extends: core.class.TxCUD, - kind: ClassifierKind.CLASS - }) - ) txes.push( createClass(core.class.Blob, { diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 735a0b0ba5..3dfd42b0cd 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -15,14 +15,14 @@ import { Analytics } from '@hcengineering/analytics' import { BackupClient, DocChunk } from './backup' -import { Account, AttachedDoc, Class, DOMAIN_MODEL, Doc, Domain, Ref, Timestamp } from './classes' +import { Account, Class, DOMAIN_MODEL, Doc, Domain, Ref, Timestamp } from './classes' import core from './component' import { Hierarchy } from './hierarchy' import { MeasureContext, MeasureMetricsContext } from './measurements' import { ModelDb } from './memdb' import type { DocumentQuery, FindOptions, FindResult, FulltextStorage, Storage, TxResult, WithLookup } from './storage' import { SearchOptions, SearchQuery, SearchResult, SortingOrder } from './storage' -import { Tx, TxCUD, TxCollectionCUD } from './tx' +import { Tx, TxCUD } from './tx' import { toFindResult } from './utils' const transactionThreshold = 500 @@ -307,9 +307,8 @@ export async function createClient ( // if we have attachment document create/delete we need to full refresh, since some derived data could be missing for (const tx of atxes) { if ( - tx._class === core.class.TxCollectionCUD && - ((tx as TxCollectionCUD).tx._class === core.class.TxCreateDoc || - (tx as TxCollectionCUD).tx._class === core.class.TxRemoveDoc) + (tx as TxCUD).attachedTo !== undefined && + (tx._class === core.class.TxCreateDoc || tx._class === core.class.TxRemoveDoc) ) { needFullRefresh = true break diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index baed6b65de..4ed7db7dff 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -60,7 +60,6 @@ import type { Tx, TxApplyIf, TxCUD, - TxCollectionCUD, TxCreateDoc, TxMixin, TxModelUpgrade, @@ -97,7 +96,6 @@ export default plugin(coreId, { TxApplyIf: '' as Ref>, TxCUD: '' as Ref>>, TxCreateDoc: '' as Ref>>, - TxCollectionCUD: '' as Ref>>, TxMixin: '' as Ref>>, TxUpdateDoc: '' as Ref>>, TxRemoveDoc: '' as Ref>>, diff --git a/packages/core/src/memdb.ts b/packages/core/src/memdb.ts index e04b712caa..471e940869 100644 --- a/packages/core/src/memdb.ts +++ b/packages/core/src/memdb.ts @@ -15,12 +15,12 @@ import { PlatformError, Severity, Status } from '@hcengineering/platform' import { Lookup, MeasureContext, ReverseLookups, getObjectValue } from '.' -import type { Account, AttachedDoc, Class, Doc, Ref } from './classes' +import type { Account, Class, Doc, Ref } from './classes' import core from './component' import { Hierarchy } from './hierarchy' import { checkMixinKey, matchQuery, resultSort } from './query' import type { DocumentQuery, FindOptions, FindResult, LookupData, Storage, TxResult, WithLookup } from './storage' -import type { Tx, TxCollectionCUD, TxCreateDoc, TxMixin, TxRemoveDoc, TxUpdateDoc } from './tx' +import type { Tx, TxCreateDoc, TxMixin, TxRemoveDoc, TxUpdateDoc } from './tx' import { TxProcessor } from './tx' import { toFindResult } from './utils' @@ -363,25 +363,6 @@ export class ModelDb extends MemDb { case core.class.TxCreateDoc: this.addDoc(TxProcessor.createDoc2Doc(tx as TxCreateDoc, clone)) break - case core.class.TxCollectionCUD: { - // We need update only create transactions to contain attached, attachedToClass. - const cud = tx as TxCollectionCUD> - if (cud.tx._class === core.class.TxCreateDoc) { - const createTx = cud.tx as TxCreateDoc - const d: TxCreateDoc = { - ...createTx, - attributes: { - ...createTx.attributes, - attachedTo: cud.objectId, - attachedToClass: cud.objectClass, - collection: cud.collection - } - } - this.addDoc(TxProcessor.createDoc2Doc(d as TxCreateDoc, clone)) - } - this.addTxes(ctx, [cud.tx], clone) - break - } case core.class.TxUpdateDoc: { const cud = tx as TxUpdateDoc const doc = this.findObject(cud.objectId) diff --git a/packages/core/src/operations.ts b/packages/core/src/operations.ts index c8d40c1c62..725e87c322 100644 --- a/packages/core/src/operations.ts +++ b/packages/core/src/operations.ts @@ -123,7 +123,7 @@ export class TxOperations implements Omit { modifiedBy ) await this.tx(tx) - return tx.tx.objectId as unknown as Ref

+ return tx.objectId as unknown as Ref

} async updateCollection( @@ -148,7 +148,7 @@ export class TxOperations implements Omit { modifiedBy ) await this.tx(tx) - return tx.objectId + return attachedTo } async removeCollection( @@ -171,7 +171,7 @@ export class TxOperations implements Omit { modifiedBy ) await this.tx(tx) - return tx.objectId + return attachedTo } updateDoc( diff --git a/packages/core/src/tx.ts b/packages/core/src/tx.ts index 51202c0fa9..13df6a1b16 100644 --- a/packages/core/src/tx.ts +++ b/packages/core/src/tx.ts @@ -88,6 +88,9 @@ export interface TxModelUpgrade extends Tx {} export interface TxCUD extends Tx { objectId: Ref objectClass: Ref> + attachedTo?: Ref + attachedToClass?: Ref> + collection?: string } /** @@ -97,17 +100,6 @@ export interface TxCreateDoc extends TxCUD { attributes: Data } -/** - * - * Will perform create/update/delete of attached documents. - * - * @public - */ -export interface TxCollectionCUD extends TxCUD { - collection: string - tx: TxCUD

-} - /** * @public */ @@ -327,9 +319,6 @@ export abstract class TxProcessor implements WithTx { case core.class.TxCreateDoc: result.push(await this.txCreateDoc(tx as TxCreateDoc)) break - case core.class.TxCollectionCUD: - result.push(await this.txCollectionCUD(tx as TxCollectionCUD)) - break case core.class.TxUpdateDoc: result.push(await this.txUpdateDoc(tx as TxUpdateDoc)) break @@ -348,9 +337,19 @@ export abstract class TxProcessor implements WithTx { } static createDoc2Doc(tx: TxCreateDoc, doClone = true): T { + const attached = + tx.attachedTo !== undefined + ? { + attachedTo: tx.attachedTo, + attachedToClass: tx.attachedToClass, + collection: tx.collection + } + : {} + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return { ...(doClone ? clone(tx.attributes) : tx.attributes), + ...attached, _id: tx.objectId, _class: tx.objectClass, space: tx.objectSpace, @@ -400,22 +399,10 @@ export abstract class TxProcessor implements WithTx { static buildDoc2Doc(txes: Tx[]): D | undefined { let doc: Doc - let createTx = txes.find((tx) => tx._class === core.class.TxCreateDoc) - if (createTx === undefined) { - const collectionTxes = txes.filter((tx) => tx._class === core.class.TxCollectionCUD) as Array< - TxCollectionCUD - > - const collectionCreateTx = collectionTxes.find((p) => p.tx._class === core.class.TxCreateDoc) - if (collectionCreateTx === undefined) return - createTx = TxProcessor.extractTx(collectionCreateTx) - } + const createTx = txes.find((tx) => tx._class === core.class.TxCreateDoc) if (createTx === undefined) return - const objectId = (createTx as TxCreateDoc).objectId doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc) - for (let tx of txes) { - if ((tx as TxCUD).objectId !== objectId && tx._class === core.class.TxCollectionCUD) { - tx = TxProcessor.extractTx(tx) - } + for (const tx of txes) { if (tx._class === core.class.TxUpdateDoc) { doc = TxProcessor.updateDoc2Doc(doc, tx as TxUpdateDoc) } else if (tx._class === core.class.TxMixin) { @@ -431,27 +418,10 @@ export abstract class TxProcessor implements WithTx { _class === core.class.TxCreateDoc || _class === core.class.TxUpdateDoc || _class === core.class.TxRemoveDoc || - _class === core.class.TxCollectionCUD || _class === core.class.TxMixin ) } - static extractTx (tx: Tx): Tx { - if (tx._class === core.class.TxCollectionCUD) { - const ctx = tx as TxCollectionCUD - if (ctx.tx._class === core.class.TxCreateDoc) { - const create = ctx.tx as TxCreateDoc - create.attributes.attachedTo = ctx.objectId - create.attributes.attachedToClass = ctx.objectClass - create.attributes.collection = ctx.collection - return create - } - return ctx.tx - } - - return tx - } - static txHasUpdate(tx: TxUpdateDoc, attribute: string): boolean { const ops = tx.operations if ((ops as any)[attribute] !== undefined) return true @@ -472,24 +442,6 @@ export abstract class TxProcessor implements WithTx { protected abstract txUpdateDoc (tx: TxUpdateDoc): Promise protected abstract txRemoveDoc (tx: TxRemoveDoc): Promise protected abstract txMixin (tx: TxMixin): Promise - - protected txCollectionCUD (tx: TxCollectionCUD): Promise { - // We need update only create transactions to contain attached, attachedToClass. - if (tx.tx._class === core.class.TxCreateDoc) { - const createTx = tx.tx as TxCreateDoc - const d: TxCreateDoc = { - ...createTx, - attributes: { - ...createTx.attributes, - attachedTo: tx.objectId, - attachedToClass: tx.objectClass, - collection: tx.collection - } - } - return this.txCreateDoc(d) - } - return this.tx(tx.tx) - } } /** @@ -534,18 +486,14 @@ export class TxFactory { tx: TxCUD

, modifiedOn?: Timestamp, modifiedBy?: Ref - ): TxCollectionCUD { + ): TxCUD

{ return { - _id: generateId(), - _class: core.class.TxCollectionCUD, - space: this.txSpace, - objectId, - objectClass: _class, - objectSpace: space, - modifiedOn: modifiedOn ?? Date.now(), - modifiedBy: modifiedBy ?? this.account, + ...tx, collection, - tx + attachedTo: objectId, + attachedToClass: _class, + modifiedOn: modifiedOn ?? Date.now(), + modifiedBy: modifiedBy ?? this.account } } diff --git a/packages/presentation/src/utils.ts b/packages/presentation/src/utils.ts index 005e2e6883..eb86633c8e 100644 --- a/packages/presentation/src/utils.ts +++ b/packages/presentation/src/utils.ts @@ -119,7 +119,7 @@ class UIClient extends TxOperations implements Client, OptimisticTxes { this.pendingTxes.delete(t._id) // Only CUD tx can be pending now - const innerTx = TxProcessor.extractTx(t) as TxCUD + const innerTx = t as TxCUD if (innerTx._class === core.class.TxCreateDoc) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete @@ -206,7 +206,7 @@ class UIClient extends TxOperations implements Client, OptimisticTxes { return } - const innerTx = TxProcessor.extractTx(tx) as TxCUD + const innerTx = tx as TxCUD // Can pre-build some configuration later from the model if this will be too slow. const instantTxes = this.getHierarchy().classHierarchyMixin(innerTx.objectClass, plugin.mixin.InstantTransactions) if (instantTxes?.txClasses.includes(innerTx._class) !== true) { diff --git a/packages/query/src/__tests__/minmodel.ts b/packages/query/src/__tests__/minmodel.ts index b03547ebd0..fffb687a68 100644 --- a/packages/query/src/__tests__/minmodel.ts +++ b/packages/query/src/__tests__/minmodel.ts @@ -188,13 +188,6 @@ export function genMinModel (): TxCUD[] { kind: ClassifierKind.CLASS }) ) - txes.push( - createClass(core.class.TxCollectionCUD, { - label: 'TxCollectionCUD' as IntlString, - extends: core.class.TxCUD, - kind: ClassifierKind.CLASS - }) - ) txes.push( createClass(core.class.TxMixin, { label: 'TxMixin' as IntlString, diff --git a/packages/query/src/__tests__/query.test.ts b/packages/query/src/__tests__/query.test.ts index 89a05facfb..f8bd46df54 100644 --- a/packages/query/src/__tests__/query.test.ts +++ b/packages/query/src/__tests__/query.test.ts @@ -677,24 +677,33 @@ describe('query', () => { ) ) } - const pp = new Promise((resolve) => { - liveQuery.query( - test.class.TestComment, - { _id: parentComment }, - (result) => { - attempt++ - const comment = result[0] - if (comment !== undefined) { - expect((comment.$lookup as any)?.comments).toHaveLength(childLength - attempt) - } - if (attempt === childLength) { - resolve(null) - } - }, - { lookup: { _id: { comments: test.class.TestComment } } } - ) + + let secondPromise: Promise | undefined + const firstCallback = new Promise((resolve) => { + secondPromise = new Promise((_resolve) => { + liveQuery.query( + test.class.TestComment, + { _id: parentComment }, + (result) => { + attempt++ + if (attempt === 0) { + resolve() + } + const comment = result[0] + if (comment !== undefined) { + expect((comment.$lookup as any)?.comments).toHaveLength(childLength - attempt) + } + if (attempt === childLength) { + _resolve() + } + }, + { lookup: { _id: { comments: test.class.TestComment } } } + ) + }) }) + await firstCallback + for (const child of childs) { await factory.removeCollection( test.class.TestComment, @@ -705,7 +714,7 @@ describe('query', () => { 'comments' ) } - await pp + await secondPromise }) it('lookup query update doc', async () => { diff --git a/packages/query/src/index.ts b/packages/query/src/index.ts index 33ce6edd2d..c88bfe84c5 100644 --- a/packages/query/src/index.ts +++ b/packages/query/src/index.ts @@ -15,7 +15,6 @@ import { Analytics } from '@hcengineering/analytics' import core, { - AttachedDoc, BulkUpdateEvent, Class, Client, @@ -38,7 +37,6 @@ import core, { Space, Timestamp, Tx, - TxCollectionCUD, TxCreateDoc, TxMixin, TxProcessor, @@ -672,41 +670,6 @@ export class LiveQuery implements WithTx, Client { return {} } - protected async txCollectionCUD ( - tx: TxCollectionCUD, - docCache: Map - ): Promise { - for (const queries of this.queries) { - const isTx = this.client.getHierarchy().isDerived(queries[0], core.class.Tx) - for (const q of queries[1]) { - if (isTx) { - // handle add since Txes are immutable - await this.handleDocAdd(q, tx, true, docCache) - continue - } - - if (tx.tx._class === core.class.TxCreateDoc) { - const createTx = tx.tx as TxCreateDoc - const d: TxCreateDoc = { - ...createTx, - attributes: { - ...createTx.attributes, - attachedTo: tx.objectId, - attachedToClass: tx.objectClass, - collection: tx.collection - } - } - await this.handleDocAdd(q, TxProcessor.createDoc2Doc(d), true, docCache) - } else if (tx.tx._class === core.class.TxUpdateDoc) { - await this.handleDocUpdate(q, tx.tx as unknown as TxUpdateDoc, docCache) - } else if (tx.tx._class === core.class.TxRemoveDoc) { - await this.handleDocRemove(q, tx.tx as unknown as TxRemoveDoc) - } - } - } - return {} - } - async txUpdateDoc (tx: TxUpdateDoc, docCache: Map): Promise { for (const queries of this.queries) { const isTx = this.client.getHierarchy().isDerived(queries[0], core.class.Tx) @@ -1278,8 +1241,6 @@ export class LiveQuery implements WithTx, Client { switch (tx._class) { case core.class.TxCreateDoc: return await this.txCreateDoc(tx as TxCreateDoc, docCache) - case core.class.TxCollectionCUD: - return await this.txCollectionCUD(tx as TxCollectionCUD, docCache) case core.class.TxUpdateDoc: return await this.txUpdateDoc(tx as TxUpdateDoc, docCache) case core.class.TxRemoveDoc: diff --git a/plugins/board-resources/src/components/editor/CardActions.svelte b/plugins/board-resources/src/components/editor/CardActions.svelte index 4031cd767c..03d8e7c15d 100644 --- a/plugins/board-resources/src/components/editor/CardActions.svelte +++ b/plugins/board-resources/src/components/editor/CardActions.svelte @@ -39,7 +39,7 @@ } } else { let targetState: Status | undefined = undefined - const txes = await client.findAll(core.class.Tx, { 'tx.objectId': value._id }) + const txes = await client.findAll(core.class.Tx, { objectId: value._id }) while (txes.length) { const doc = TxProcessor.buildDoc2Doc(txes) if (doc === undefined) { diff --git a/plugins/chunter-resources/src/components/ReverseChannelScrollView.svelte b/plugins/chunter-resources/src/components/ReverseChannelScrollView.svelte index 27ab50660a..9863ceee4d 100644 --- a/plugins/chunter-resources/src/components/ReverseChannelScrollView.svelte +++ b/plugins/chunter-resources/src/components/ReverseChannelScrollView.svelte @@ -21,7 +21,7 @@ Space, Timestamp, Tx, - TxCollectionCUD, + TxCUD, TxProcessor } from '@hcengineering/core' import activity, { ActivityMessage } from '@hcengineering/activity' @@ -546,11 +546,9 @@ } const newMessageTxListener = (tx: Tx): void => { - if (tx._class !== core.class.TxCollectionCUD) return - const ctx = tx as TxCollectionCUD - if (ctx.objectId !== doc._id) return - const etx = TxProcessor.extractTx(tx) - if (etx._class !== core.class.TxCreateDoc) return + const ctx = tx as TxCUD + if (ctx.attachedTo !== doc._id) return + if (ctx._class !== core.class.TxCreateDoc) return if (shouldScrollToNew) { void wait().then(scrollToNewMessages) } diff --git a/plugins/task-resources/src/components/Dashboard.svelte b/plugins/task-resources/src/components/Dashboard.svelte index c81982ca07..3376dde2b9 100644 --- a/plugins/task-resources/src/components/Dashboard.svelte +++ b/plugins/task-resources/src/components/Dashboard.svelte @@ -49,15 +49,14 @@ return } txQuery.query( - core.class.TxCollectionCUD, + core.class.TxCreateDoc, { objectSpace: space, - 'tx._class': core.class.TxCreateDoc, - 'tx.objectClass': _class, + objectClass: _class, modifiedOn: { $gte: modified } }, (result) => { - ids = result.map((p) => p.tx.objectId) as Ref[] + ids = result.map((p) => p.objectId) as Ref[] } ) } diff --git a/plugins/test-management-resources/src/utils.ts b/plugins/test-management-resources/src/utils.ts index 63fa18bb32..823692ebf8 100644 --- a/plugins/test-management-resources/src/utils.ts +++ b/plugins/test-management-resources/src/utils.ts @@ -14,7 +14,7 @@ // import { type Contact } from '@hcengineering/contact' -import core, { type Doc, type Ref, type TxCollectionCUD, type TxCreateDoc, type TxUpdateDoc } from '@hcengineering/core' +import core, { type Doc, type Ref, type TxCreateDoc, type TxUpdateDoc } from '@hcengineering/core' import { getClient } from '@hcengineering/presentation' import { showPopup } from '@hcengineering/ui' import { type TestProject, type TestCase, type TestSuite } from '@hcengineering/test-management' @@ -30,20 +30,19 @@ export async function getPreviousAssignees (objectId: Ref | undefined): Pro } const client = getClient() const createTx = ( - await client.findAll>(core.class.TxCollectionCUD, { - 'tx.objectId': objectId, - 'tx._class': core.class.TxCreateDoc + await client.findAll>(core.class.TxCreateDoc, { + objectId: objectId as Ref }) )[0] - const updateTxes = await client.findAll>( - core.class.TxCollectionCUD, - { 'tx.objectId': objectId, 'tx._class': core.class.TxUpdateDoc, 'tx.operations.assignee': { $exists: true } }, + const updateTxes = await client.findAll>( + core.class.TxUpdateDoc, + { objectId: objectId as Ref, 'operations.assignee': { $exists: true } }, { sort: { modifiedOn: -1 } } ) const set = new Set>() - const createAssignee = (createTx?.tx as TxCreateDoc)?.attributes?.assignee + const createAssignee = createTx?.attributes?.assignee for (const tx of updateTxes) { - const assignee = (tx.tx as TxUpdateDoc).operations.assignee + const assignee = tx.operations.assignee if (assignee == null) continue set.add(assignee) } diff --git a/plugins/time-resources/src/components/team/calendar/TeamCalendar.svelte b/plugins/time-resources/src/components/team/calendar/TeamCalendar.svelte index f69a80c0c3..95b2c9e4f5 100644 --- a/plugins/time-resources/src/components/team/calendar/TeamCalendar.svelte +++ b/plugins/time-resources/src/components/team/calendar/TeamCalendar.svelte @@ -25,7 +25,6 @@ Tx, TxCUD, TxCreateDoc, - TxProcessor, TxUpdateDoc, getCurrentAccount } from '@hcengineering/core' @@ -66,8 +65,7 @@ { modifiedBy: { $in: Array.from(personAccounts.map((it) => it._id)) }, modifiedOn: { $gt: fromDate, $lt: toDate } }, (res) => { const map = new Map, Tx[]>() - for (const _t of res) { - const t = TxProcessor.extractTx(_t) + for (const t of res) { const account = t.createdBy ?? t.modifiedBy map.set(account, [...(map.get(account) ?? []), t]) } diff --git a/plugins/tracker-resources/src/components/issues/AssigneeEditor.svelte b/plugins/tracker-resources/src/components/issues/AssigneeEditor.svelte index f7453f3a8e..7717b7370a 100644 --- a/plugins/tracker-resources/src/components/issues/AssigneeEditor.svelte +++ b/plugins/tracker-resources/src/components/issues/AssigneeEditor.svelte @@ -92,7 +92,7 @@ func: async () => { const r: Ref[] = [] for (const d of cdocs) { - r.push(...(await getPreviousAssignees(d._id))) + r.push(...(await getPreviousAssignees(d._id as Ref))) } return r } diff --git a/plugins/tracker-resources/src/components/issues/IssueStatusActivity.svelte b/plugins/tracker-resources/src/components/issues/IssueStatusActivity.svelte index 92422b6ad3..a46f33d618 100644 --- a/plugins/tracker-resources/src/components/issues/IssueStatusActivity.svelte +++ b/plugins/tracker-resources/src/components/issues/IssueStatusActivity.svelte @@ -1,14 +1,5 @@ + +

+ {#if hasText(object.prompt)} +
+ + {object.prompt} + +
+ {/if} + {#each object.questions ?? [] as question, index} + {#if isQuestionValid(question)} +
+ +
+ {/if} + {/each} +
+ + diff --git a/plugins/survey-resources/src/components/EditPollPanel.svelte b/plugins/survey-resources/src/components/EditPollPanel.svelte new file mode 100644 index 0000000000..8cacdaa163 --- /dev/null +++ b/plugins/survey-resources/src/components/EditPollPanel.svelte @@ -0,0 +1,114 @@ + + + +{#if object} + { + dispatch('close') + }} + withoutInput={readonly} + > + + {#if !embedded}{/if} + +
{object.name}
+
+
+ + + {#if !readonly} + {#if !(object.isCompleted ?? false)} +