From 5353a06b07cd5e8d9bfd9b81471472f4d86b3db8 Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Mon, 18 Nov 2024 17:44:09 +0500 Subject: [PATCH] Move model (#7185) Signed-off-by: Denis Bykhov --- models/core/src/migration.ts | 32 +++++-- models/document/src/migration.ts | 8 +- models/guest/src/migration.ts | 12 +-- models/lead/src/migration.ts | 13 ++- models/notification/src/migration.ts | 6 +- models/recruit/src/migration.ts | 4 +- models/request/src/migration.ts | 12 +-- models/survey/src/plugin.ts | 4 +- models/task/src/migration.ts | 49 ++++++---- models/tracker/src/migration.ts | 34 +++---- packages/core/src/classes.ts | 5 ++ plugins/workbench-resources/src/connect.ts | 30 +------ qms-tests/prepare.sh | 6 +- server/mongo/src/storage.ts | 100 +++++++++++++++------ server/postgres/src/schemas.ts | 3 +- server/postgres/src/storage.ts | 23 ++++- server/postgres/src/utils.ts | 6 +- tests/prepare.sh | 4 - 18 files changed, 206 insertions(+), 145 deletions(-) diff --git a/models/core/src/migration.ts b/models/core/src/migration.ts index 815481049e..ed7a8c5b5d 100644 --- a/models/core/src/migration.ts +++ b/models/core/src/migration.ts @@ -15,25 +15,26 @@ import { saveCollaborativeDoc } from '@hcengineering/collaboration' import core, { - type Class, + collaborativeDocParse, + coreId, + DOMAIN_MODEL_TX, DOMAIN_SPACE, DOMAIN_STATUS, DOMAIN_TX, - MeasureMetricsContext, - RateLimiter, - type Ref, - type TxCUD, - collaborativeDocParse, - coreId, generateId, makeCollaborativeDoc, + MeasureMetricsContext, + RateLimiter, type AnyAttribute, + type Class, type Doc, type Domain, type MeasureContext, + type Ref, type Space, type Status, - type TxCreateDoc + type TxCreateDoc, + type TxCUD } from '@hcengineering/core' import { createDefaultSpace, @@ -304,6 +305,7 @@ export const coreOperation: MigrateOperation = { if (txes.length === 0) break for (const tx of txes) { processed++ + const { _id, ...ops } = (tx as any).tx await client.update( DOMAIN_TX, { _id: tx._id }, @@ -311,7 +313,7 @@ export const coreOperation: MigrateOperation = { $set: { attachedTo: tx.objectId, attachedToClass: tx.objectClass, - ...(tx as any).tx + ...ops } } ) @@ -321,6 +323,18 @@ export const coreOperation: MigrateOperation = { } } } + }, + { + state: 'move-model-txes', + func: async (client) => { + await client.move( + DOMAIN_TX, + { + objectSpace: core.space.Model + }, + DOMAIN_MODEL_TX + ) + } } ]) }, diff --git a/models/document/src/migration.ts b/models/document/src/migration.ts index 530ea6d631..e05caea6f9 100644 --- a/models/document/src/migration.ts +++ b/models/document/src/migration.ts @@ -13,11 +13,11 @@ // limitations under the License. // -import { type CollaborativeDoc, DOMAIN_TX, MeasureMetricsContext, SortingOrder } from '@hcengineering/core' -import { type DocumentSnapshot, type Document, type Teamspace } from '@hcengineering/document' +import { DOMAIN_MODEL_TX, MeasureMetricsContext, SortingOrder, type CollaborativeDoc } from '@hcengineering/core' +import { type Document, type DocumentSnapshot, type Teamspace } from '@hcengineering/document' import { - tryMigrate, migrateSpaceRanks, + tryMigrate, type MigrateOperation, type MigrateUpdate, type MigrationClient, @@ -75,7 +75,7 @@ async function migrateTeamspacesMixins (client: MigrationClient): Promise const newSpaceTypeMixin = document.mixin.DefaultTeamspaceTypeData await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectClass: core.class.Attribute, 'attributes.attributeOf': oldSpaceTypeMixin diff --git a/models/guest/src/migration.ts b/models/guest/src/migration.ts index 12e08146aa..17e8e31380 100644 --- a/models/guest/src/migration.ts +++ b/models/guest/src/migration.ts @@ -1,9 +1,9 @@ import { AccountRole, - DOMAIN_TX, + DOMAIN_MODEL_TX, + type Account, type Ref, type Space, - type Account, type TxCreateDoc, type TxUpdateDoc } from '@hcengineering/core' @@ -30,13 +30,13 @@ export const guestOperation: MigrateOperation = { 1: AccountRole.Maintainer, 2: AccountRole.Owner } - const createTxes = await client.find>(DOMAIN_TX, { + const createTxes = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxCreateDoc, 'attributes.role': { $in: [0, 1, 2] } }) for (const tx of createTxes) { await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { _id: tx._id }, @@ -47,13 +47,13 @@ export const guestOperation: MigrateOperation = { } ) } - const updateTxes = await client.find>(DOMAIN_TX, { + const updateTxes = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxUpdateDoc, 'operations.role': { $in: [0, 1, 2] } }) for (const tx of updateTxes) { await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { _id: tx._id }, diff --git a/models/lead/src/migration.ts b/models/lead/src/migration.ts index 59155e68b0..7f77fd49da 100644 --- a/models/lead/src/migration.ts +++ b/models/lead/src/migration.ts @@ -13,7 +13,14 @@ // limitations under the License. // -import { AccountRole, DOMAIN_TX, makeCollaborativeDoc, TxOperations, type Ref, type Status } from '@hcengineering/core' +import { + AccountRole, + DOMAIN_MODEL_TX, + makeCollaborativeDoc, + TxOperations, + type Ref, + type Status +} from '@hcengineering/core' import { leadId, type Lead } from '@hcengineering/lead' import { tryMigrate, @@ -26,7 +33,7 @@ import { import core, { DOMAIN_SPACE } from '@hcengineering/model-core' import contact, { DOMAIN_CONTACT } from '@hcengineering/model-contact' -import task, { DOMAIN_TASK, createSequence, migrateDefaultStatusesBase } from '@hcengineering/model-task' +import task, { createSequence, DOMAIN_TASK, migrateDefaultStatusesBase } from '@hcengineering/model-task' import lead from './plugin' import { defaultLeadStatuses } from './spaceType' @@ -110,7 +117,7 @@ async function migrateDefaultTypeMixins (client: MigrationClient): Promise const newTaskTypeMixin = lead.mixin.LeadTypeData await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectClass: core.class.Attribute, 'attributes.attributeOf': oldSpaceTypeMixin diff --git a/models/notification/src/migration.ts b/models/notification/src/migration.ts index 19bc06698e..f30583e707 100644 --- a/models/notification/src/migration.ts +++ b/models/notification/src/migration.ts @@ -13,6 +13,8 @@ // limitations under the License. // +import chunter from '@hcengineering/chunter' +import contact, { type PersonSpace } from '@hcengineering/contact' import core, { DOMAIN_TX, type Class, type Doc, type DocumentQuery, type Ref, type Space } from '@hcengineering/core' import { migrateSpace, @@ -28,11 +30,9 @@ import notification, { type InboxNotification } from '@hcengineering/notification' import { DOMAIN_PREFERENCE } from '@hcengineering/preference' -import contact, { type PersonSpace } from '@hcengineering/contact' -import chunter from '@hcengineering/chunter' -import { DOMAIN_DOC_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_USER_NOTIFY } from './index' import { DOMAIN_SPACE } from '@hcengineering/model-core' +import { DOMAIN_DOC_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_USER_NOTIFY } from './index' export async function removeNotifications ( client: MigrationClient, diff --git a/models/recruit/src/migration.ts b/models/recruit/src/migration.ts index 87313df3db..21c5c15633 100644 --- a/models/recruit/src/migration.ts +++ b/models/recruit/src/migration.ts @@ -15,7 +15,7 @@ import { getCategories } from '@anticrm/skillset' import core, { - DOMAIN_TX, + DOMAIN_MODEL_TX, toIdMap, TxOperations, type Doc, @@ -149,7 +149,7 @@ async function migrateDefaultTypeMixins (client: MigrationClient): Promise const newTaskTypeMixin = recruit.mixin.ApplicantTypeData await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectClass: core.class.Attribute, 'attributes.attributeOf': oldSpaceTypeMixin diff --git a/models/request/src/migration.ts b/models/request/src/migration.ts index f7fbda4185..7ff603449a 100644 --- a/models/request/src/migration.ts +++ b/models/request/src/migration.ts @@ -12,18 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. // -import core, { DOMAIN_TX, type Ref, type TxCreateDoc } from '@hcengineering/core' -import request, { requestId, type Request } from '@hcengineering/request' +import contact, { type Person, type PersonAccount } from '@hcengineering/contact' +import core, { DOMAIN_MODEL_TX, type Ref, type TxCreateDoc } from '@hcengineering/core' import { - type MigrateUpdate, - type MigrationDocumentQuery, tryMigrate, type MigrateOperation, + type MigrateUpdate, type MigrationClient, + type MigrationDocumentQuery, type MigrationUpgradeClient, type ModelLogger } from '@hcengineering/model' -import contact, { type Person, type PersonAccount } from '@hcengineering/contact' +import request, { requestId, type Request } from '@hcengineering/request' import { DOMAIN_REQUEST } from '.' @@ -32,7 +32,7 @@ async function migrateRequestPersonAccounts (client: MigrationClient): Promise(DOMAIN_REQUEST, { _class: { $in: descendants } }) - const personAccountsCreateTxes = await client.find(DOMAIN_TX, { + const personAccountsCreateTxes = await client.find(DOMAIN_MODEL_TX, { _class: core.class.TxCreateDoc, objectClass: contact.class.PersonAccount }) diff --git a/models/survey/src/plugin.ts b/models/survey/src/plugin.ts index d6011d4409..70968dc5b3 100644 --- a/models/survey/src/plugin.ts +++ b/models/survey/src/plugin.ts @@ -13,8 +13,6 @@ // limitations under the License. // -import { mergeIds } from '@hcengineering/platform' -import { surveyId } from '@hcengineering/survey' import survey from '@hcengineering/survey-resources/src/plugin' -export default mergeIds(surveyId, survey, {}) +export default survey diff --git a/models/task/src/migration.ts b/models/task/src/migration.ts index 47a20ea420..ffb516d8de 100644 --- a/models/task/src/migration.ts +++ b/models/task/src/migration.ts @@ -15,6 +15,7 @@ import activity, { type DocUpdateMessage } from '@hcengineering/activity' import { + DOMAIN_MODEL_TX, DOMAIN_STATUS, DOMAIN_TX, TxOperations, @@ -94,7 +95,7 @@ export async function migrateDefaultStatusesBase ( // 3. More than one type (one system and one custom) - the tool is running after the WS upgrade. // Not supported for now. Alternatively - Proceed with (2) scenario for the custom one. Delete it in the end. - const defaultTypes = await client.find>(DOMAIN_TX, { + const defaultTypes = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxCreateDoc, objectId: defaultTypeId, objectSpace: core.space.Model, @@ -120,7 +121,7 @@ export async function migrateDefaultStatusesBase ( // and not modified by user if (defaultType.attributes.tasks.length === 1 && defaultType.attributes.tasks[0] === defaultTaskTypeId) { const defaultTaskType = ( - await client.find>(DOMAIN_TX, { + await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxCreateDoc, objectId: defaultTaskTypeId, objectSpace: core.space.Model, @@ -132,7 +133,7 @@ export async function migrateDefaultStatusesBase ( logger.log('Moving the existing default type created by ConfigUser to a system one', '') logger.log('Moving the existing default task type created by ConfigUser to a system one', '') await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { _id: defaultTaskType._id }, { $set: { @@ -142,7 +143,7 @@ export async function migrateDefaultStatusesBase ( ) await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { _id: defaultType._id }, { $set: { @@ -172,7 +173,7 @@ export async function migrateDefaultStatusesBase ( logger.log('Moving the existing default type to a custom one', '') const newId = defaultType.objectId + '-custom' await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { _id: defaultType._id }, { $set: { @@ -182,7 +183,7 @@ export async function migrateDefaultStatusesBase ( } ) await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectId: defaultType.objectId, objectSpace: core.space.Model @@ -194,7 +195,7 @@ export async function migrateDefaultStatusesBase ( } ) await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectId: { $in: defaultType.attributes.tasks }, objectSpace: core.space.Model, @@ -294,7 +295,7 @@ export async function migrateDefaultStatusesBase ( // 1. Update all update TXes with statuses // 2. Update all push TXes with statuses - const projectTypeStatusesCreates = await client.find>(DOMAIN_TX, { + const projectTypeStatusesCreates = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxCreateDoc, objectClass: task.class.ProjectType, objectSpace: core.space.Model, @@ -312,11 +313,11 @@ export async function migrateDefaultStatusesBase ( } counter++ - await client.update(DOMAIN_TX, { _id: ptsCreate._id }, { $set: { 'attributes.statuses': newUpdateStatuses } }) + await client.update(DOMAIN_MODEL_TX, { _id: ptsCreate._id }, { $set: { 'attributes.statuses': newUpdateStatuses } }) } logger.log('projectTypeStatusesCreates updated: ', counter) - const projectTypeStatusesUpdates = await client.find>(DOMAIN_TX, { + const projectTypeStatusesUpdates = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxUpdateDoc, objectId: { $in: projectTypeStatusesCreates.map((sc) => sc.objectId) }, objectClass: task.class.ProjectType, @@ -334,11 +335,11 @@ export async function migrateDefaultStatusesBase ( } counter++ - await client.update(DOMAIN_TX, { _id: ptsUpdate._id }, { $set: { 'operations.statuses': newUpdateStatuses } }) + await client.update(DOMAIN_MODEL_TX, { _id: ptsUpdate._id }, { $set: { 'operations.statuses': newUpdateStatuses } }) } logger.log('projectTypeStatusesUpdates updated: ', counter) - const projectTypeStatusesPushes = await client.find>(DOMAIN_TX, { + const projectTypeStatusesPushes = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxUpdateDoc, objectId: { $in: projectTypeStatusesCreates.map((sc) => sc.objectId) }, objectClass: task.class.ProjectType, @@ -362,7 +363,11 @@ export async function migrateDefaultStatusesBase ( } counter++ - await client.update(DOMAIN_TX, { _id: ptsUpdate._id }, { $set: { 'operations.$push.statuses': newPushStatus } }) + await client.update( + DOMAIN_MODEL_TX, + { _id: ptsUpdate._id }, + { $set: { 'operations.$push.statuses': newPushStatus } } + ) } logger.log('projectTypeStatusesPushes updated: ', counter) @@ -370,7 +375,7 @@ export async function migrateDefaultStatusesBase ( // 1. Update create TX // 2. Update all update TXes with statuses - const allTaskTypes = await client.find>(DOMAIN_TX, { + const allTaskTypes = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxCreateDoc, objectClass: taskTypeClass, 'attributes.ofClass': { $in: baseTaskClasses } @@ -387,11 +392,15 @@ export async function migrateDefaultStatusesBase ( } counter++ - await client.update(DOMAIN_TX, { _id: taskType._id }, { $set: { 'attributes.statuses': newTaskTypeStatuses } }) + await client.update( + DOMAIN_MODEL_TX, + { _id: taskType._id }, + { $set: { 'attributes.statuses': newTaskTypeStatuses } } + ) } logger.log('allTaskTypes updated: ', counter) - const allTaskTypeStatusesUpdates = await client.find>(DOMAIN_TX, { + const allTaskTypeStatusesUpdates = await client.find>(DOMAIN_MODEL_TX, { _class: core.class.TxUpdateDoc, objectClass: taskTypeClass, objectId: { $in: allTaskTypes.map((tt) => tt.objectId) }, @@ -413,7 +422,7 @@ export async function migrateDefaultStatusesBase ( counter++ await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { _id: ttsUpdate._id }, { $set: { 'operations.statuses': newTaskTypeUpdateStatuses } } ) @@ -545,6 +554,12 @@ export const taskOperation: MigrateOperation = { { objectId: { $in: missing }, objectSpace: 'task:space:Statuses' }, { $set: { objectSpace: core.space.Model } } ) + await client.update( + DOMAIN_MODEL_TX, + { objectId: { $in: missing }, objectSpace: 'task:space:Statuses' }, + { $set: { objectSpace: core.space.Model } } + ) + await client.move(DOMAIN_TX, { objectId: { $in: missing }, objectSpace: core.space.Model }, DOMAIN_MODEL_TX) } } }, diff --git a/models/tracker/src/migration.ts b/models/tracker/src/migration.ts index f6ccd71142..060ac13f44 100644 --- a/models/tracker/src/migration.ts +++ b/models/tracker/src/migration.ts @@ -13,37 +13,37 @@ // limitations under the License. // +import activity, { type DocUpdateMessage } from '@hcengineering/activity' import core, { + AccountRole, + DOMAIN_MODEL_TX, DOMAIN_STATUS, + type Ref, + type Status, type TxCreateDoc, TxOperations, generateId, - toIdMap, - DOMAIN_TX, - type Status, - type Ref, - AccountRole + toIdMap } from '@hcengineering/core' import { + type MigrateOperation, + type MigrationClient, + type MigrationUpgradeClient, type ModelLogger, createOrUpdate, tryMigrate, - tryUpgrade, - type MigrateOperation, - type MigrationClient, - type MigrationUpgradeClient + tryUpgrade } from '@hcengineering/model' -import { DOMAIN_SPACE } from '@hcengineering/model-core' -import activity, { type DocUpdateMessage } from '@hcengineering/activity' import { DOMAIN_ACTIVITY } from '@hcengineering/model-activity' +import { DOMAIN_SPACE } from '@hcengineering/model-core' import { DOMAIN_TASK, migrateDefaultStatusesBase } from '@hcengineering/model-task' import tags from '@hcengineering/tags' import task from '@hcengineering/task' -import { type IssueStatus, TimeReportDayType, trackerId, type Issue, type Project } from '@hcengineering/tracker' +import { type Issue, type IssueStatus, type Project, TimeReportDayType, trackerId } from '@hcengineering/tracker' -import tracker from './plugin' import contact from '@hcengineering/model-contact' import { classicIssueTaskStatuses } from '.' +import tracker from './plugin' async function createDefaultProject (tx: TxOperations): Promise { const current = await tx.findOne(tracker.class.Project, { @@ -280,7 +280,7 @@ async function migrateStatusesToModel (client: MigrationClient): Promise { modifiedBy } - await client.create(DOMAIN_TX, tx) + await client.create(DOMAIN_MODEL_TX, tx) } } @@ -291,7 +291,7 @@ async function migrateDefaultTypeMixins (client: MigrationClient): Promise const newTaskTypeMixin = tracker.mixin.IssueTypeData await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectClass: core.class.Attribute, 'attributes.attributeOf': oldSpaceTypeMixin @@ -350,7 +350,7 @@ async function migrateDefaultProjectOwners (client: MigrationClient): Promise { await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectClass: task.class.TaskType, 'attributes.ofClass': tracker.class.Issue, @@ -363,7 +363,7 @@ async function migrateIssueStatuses (client: MigrationClient): Promise { } ) await client.update( - DOMAIN_TX, + DOMAIN_MODEL_TX, { objectClass: core.class.Status, 'attributes.ofAttribute': tracker.attribute.IssueStatus diff --git a/packages/core/src/classes.ts b/packages/core/src/classes.ts index 027327ab44..4c33aeded2 100644 --- a/packages/core/src/classes.ts +++ b/packages/core/src/classes.ts @@ -326,6 +326,11 @@ export interface TypeAny extends Type { */ export const DOMAIN_MODEL = 'model' as Domain +/** + * @public + */ +export const DOMAIN_MODEL_TX = 'model_tx' as Domain + /** * @public */ diff --git a/plugins/workbench-resources/src/connect.ts b/plugins/workbench-resources/src/connect.ts index d9255f8ee8..eb56a0ee5e 100644 --- a/plugins/workbench-resources/src/connect.ts +++ b/plugins/workbench-resources/src/connect.ts @@ -11,7 +11,6 @@ import core, { type Account, type AccountClient, type Client, - type MeasureContext, type MeasureMetricsContext, type Version } from '@hcengineering/core' @@ -36,7 +35,7 @@ import { setMetadataLocalStorage, themeStore } from '@hcengineering/ui' -import { writable, get } from 'svelte/store' +import { get, writable } from 'svelte/store' import plugin from './plugin' import { workspaceCreating } from './utils' @@ -304,10 +303,7 @@ export async function connect (title: string): Promise { _client = newClient console.log('logging in as', email) - let me: Account | undefined = await ctx.with('get-account', {}, async () => await newClient.getAccount()) - if (me === undefined) { - me = await createEmployee(ctx, ws, me, newClient) - } + const me: Account | undefined = await ctx.with('get-account', {}, async () => await newClient.getAccount()) if (me !== undefined) { Analytics.setUser(me.email) Analytics.setTag('workspace', ws) @@ -374,28 +370,6 @@ export async function connect (title: string): Promise { return newClient } -async function createEmployee ( - ctx: MeasureContext, - ws: string, - me: Account, - newClient: AccountClient -): Promise { - const createEmployee = await getResource(login.function.CreateEmployee) - await ctx.with('create-missing-employee', {}, async () => { - await createEmployee(ws) - }) - for (let i = 0; i < 5; i++) { - me = await ctx.with('get-account', {}, async () => await newClient.getAccount()) - if (me !== undefined) { - break - } - await new Promise((resolve) => { - setTimeout(resolve, 100) - }) - } - return me -} - function clearMetadata (ws: string): void { const tokens = fetchMetadataLocalStorage(login.metadata.LoginTokens) if (tokens !== null) { diff --git a/qms-tests/prepare.sh b/qms-tests/prepare.sh index 6fcdab7295..ff95c734d6 100755 --- a/qms-tests/prepare.sh +++ b/qms-tests/prepare.sh @@ -29,10 +29,7 @@ fi ./tool.sh create-account user2 -f Kainin -l Dirak -p 1234 ./tool.sh create-account user3 -f Cain -l Velasquez -p 1234 ./tool.sh create-account user4 -f Armin -l Karmin -p 1234 -./tool.sh assign-workspace user1 sanity-ws-qms -./tool.sh assign-workspace user2 sanity-ws-qms -./tool.sh assign-workspace user3 sanity-ws-qms -./tool.sh assign-workspace user4 sanity-ws-qms + # Make user the workspace maintainer ./tool.sh confirm-email user1 ./tool.sh confirm-email user2 @@ -40,7 +37,6 @@ fi ./tool.sh confirm-email user4 ./tool.sh create-account user_qara -f Qara -l Admin -p 1234 -./tool.sh assign-workspace user_qara sanity-ws-qms ./tool.sh confirm-email user_qara ./restore-workspace.sh \ No newline at end of file diff --git a/server/mongo/src/storage.ts b/server/mongo/src/storage.ts index f87814d39d..bf99f5ca27 100644 --- a/server/mongo/src/storage.ts +++ b/server/mongo/src/storage.ts @@ -15,6 +15,7 @@ import core, { DOMAIN_MODEL, + DOMAIN_MODEL_TX, DOMAIN_TX, SortingOrder, TxProcessor, @@ -1546,6 +1547,7 @@ class MongoTxAdapter extends MongoAdapterBase implements TxAdapter { async init (): Promise { await this._db.init(DOMAIN_TX) + await this._db.init(DOMAIN_MODEL_TX) } override async tx (ctx: MeasureContext, ...tx: Tx[]): Promise { @@ -1554,34 +1556,74 @@ class MongoTxAdapter extends MongoAdapterBase implements TxAdapter { } const opName = tx.length === 1 ? 'tx-one' : 'tx' - await addOperation( - ctx, - opName, - {}, - async (ctx) => { - await ctx.with( - 'insertMany', - { domain: 'tx' }, - async () => { - try { - await this.txCollection().insertMany( - tx.map((it) => translateDoc(it)), - { - ordered: false - } - ) - } catch (err: any) { - ctx.error('failed to write tx', { error: err, message: err.message }) - } - }, + const modelTxes: Tx[] = [] + const baseTxes: Tx[] = [] + for (const _tx of tx) { + if (_tx.objectSpace === core.space.Model) { + modelTxes.push(_tx) + } else { + baseTxes.push(_tx) + } + } + if (baseTxes.length > 0) { + await addOperation( + ctx, + opName, + {}, + async (ctx) => { + await ctx.with( + 'insertMany', + { domain: 'tx' }, + async () => { + try { + await this.txCollection().insertMany( + baseTxes.map((it) => translateDoc(it)), + { + ordered: false + } + ) + } catch (err: any) { + ctx.error('failed to write tx', { error: err, message: err.message }) + } + }, - { - count: tx.length - } - ) - }, - { domain: 'tx', count: tx.length } - ) + { + count: baseTxes.length + } + ) + }, + { domain: 'tx', count: baseTxes.length } + ) + } + if (modelTxes.length > 0) { + await addOperation( + ctx, + opName, + {}, + async (ctx) => { + await ctx.with( + 'insertMany', + { domain: DOMAIN_MODEL_TX }, + async () => { + try { + await this.db.collection(DOMAIN_MODEL_TX).insertMany( + modelTxes.map((it) => translateDoc(it)), + { + ordered: false + } + ) + } catch (err: any) { + ctx.error('failed to write model tx', { error: err, message: err.message }) + } + }, + { + count: modelTxes.length + } + ) + }, + { domain: DOMAIN_MODEL_TX, count: modelTxes.length } + ) + } ctx.withSync('handleEvent', {}, () => { this.handleEvent(DOMAIN_TX, 'add', tx.length) }) @@ -1598,8 +1640,8 @@ class MongoTxAdapter extends MongoAdapterBase implements TxAdapter { @withContext('get-model') async getModel (ctx: MeasureContext): Promise { - const txCollection = this.db.collection(DOMAIN_TX) - const cursor = txCollection.find({ objectSpace: core.space.Model }, { sort: { _id: 1, modifiedOn: 1 } }) + const txCollection = this.db.collection(DOMAIN_MODEL_TX) + const cursor = txCollection.find({}, { sort: { _id: 1, modifiedOn: 1 } }) const model = await toArray(cursor) // We need to put all core.account.System transactions first const systemTx: Tx[] = [] diff --git a/server/postgres/src/schemas.ts b/server/postgres/src/schemas.ts index 65086348d0..253de0bfca 100644 --- a/server/postgres/src/schemas.ts +++ b/server/postgres/src/schemas.ts @@ -1,4 +1,4 @@ -import { DOMAIN_DOC_INDEX_STATE, DOMAIN_SPACE, DOMAIN_TX } from '@hcengineering/core' +import { DOMAIN_DOC_INDEX_STATE, DOMAIN_MODEL_TX, DOMAIN_SPACE, DOMAIN_TX } from '@hcengineering/core' export type DataType = 'bigint' | 'bool' | 'text' | 'text[]' @@ -223,6 +223,7 @@ export function translateDomain (domain: string): string { export const domainSchemas: Record = { [DOMAIN_SPACE]: spaceSchema, [DOMAIN_TX]: txSchema, + [DOMAIN_MODEL_TX]: txSchema, [translateDomain('time')]: timeSchema, [translateDomain('calendar')]: calendarSchema, [translateDomain('event')]: eventSchema, diff --git a/server/postgres/src/storage.ts b/server/postgres/src/storage.ts index 83f16c84ce..493421695b 100644 --- a/server/postgres/src/storage.ts +++ b/server/postgres/src/storage.ts @@ -22,6 +22,7 @@ import core, { type DocumentUpdate, type Domain, DOMAIN_MODEL, + DOMAIN_MODEL_TX, DOMAIN_SPACE, DOMAIN_TX, type FindOptions, @@ -1573,7 +1574,7 @@ class PostgresAdapter extends PostgresAdapterBase { class PostgresTxAdapter extends PostgresAdapterBase implements TxAdapter { async init (domains?: string[], excludeDomains?: string[]): Promise { - const resultDomains = domains ?? [DOMAIN_TX] + const resultDomains = domains ?? [DOMAIN_TX, DOMAIN_MODEL_TX] await createTables(this.client, resultDomains) this._helper.domains = new Set(resultDomains as Domain[]) } @@ -1583,7 +1584,21 @@ class PostgresTxAdapter extends PostgresAdapterBase implements TxAdapter { return [] } try { - await this.insert(ctx, DOMAIN_TX, tx) + const modelTxes: Tx[] = [] + const baseTxes: Tx[] = [] + for (const _tx of tx) { + if (_tx.objectSpace === core.space.Model) { + modelTxes.push(_tx) + } else { + baseTxes.push(_tx) + } + } + if (modelTxes.length > 0) { + await this.insert(ctx, DOMAIN_MODEL_TX, modelTxes) + } + if (baseTxes.length > 0) { + await this.insert(ctx, DOMAIN_TX, baseTxes) + } } catch (err) { console.error(err) } @@ -1592,9 +1607,9 @@ class PostgresTxAdapter extends PostgresAdapterBase implements TxAdapter { async getModel (ctx: MeasureContext): Promise { const res = await this - .client`SELECT * FROM ${this.client(translateDomain(DOMAIN_TX))} WHERE "workspaceId" = ${this.workspaceId.name} AND "objectSpace" = ${core.space.Model} ORDER BY _id ASC, "modifiedOn" ASC` + .client`SELECT * FROM ${this.client(translateDomain(DOMAIN_MODEL_TX))} WHERE "workspaceId" = ${this.workspaceId.name} ORDER BY _id ASC, "modifiedOn" ASC` - const model = res.map((p) => parseDoc(p as any, DOMAIN_TX)) + const model = res.map((p) => parseDoc(p as any, DOMAIN_MODEL_TX)) // We need to put all core.account.System transactions first const systemTx: Tx[] = [] const userTx: Tx[] = [] diff --git a/server/postgres/src/utils.ts b/server/postgres/src/utils.ts index 9a5499d569..76f6855bd6 100644 --- a/server/postgres/src/utils.ts +++ b/server/postgres/src/utils.ts @@ -411,8 +411,7 @@ export function parseDocWithProjection ( } else { ;(rest as any)[key] = null } - } - if (schema[key] !== undefined && schema[key].type === 'bigint') { + } else if (schema[key] !== undefined && schema[key].type === 'bigint') { ;(rest as any)[key] = Number.parseInt((rest as any)[key]) } } @@ -443,8 +442,7 @@ export function parseDoc (doc: DBDoc, domain: string): T { } else { ;(rest as any)[key] = null } - } - if (schema[key] !== undefined && schema[key].type === 'bigint') { + } else if (schema[key] !== undefined && schema[key].type === 'bigint') { ;(rest as any)[key] = Number.parseInt((rest as any)[key]) } } diff --git a/tests/prepare.sh b/tests/prepare.sh index ebb434f584..197952e063 100755 --- a/tests/prepare.sh +++ b/tests/prepare.sh @@ -25,10 +25,6 @@ fi ./tool.sh create-account user2 -f Kainin -l Dirak -p 1234 ./tool.sh create-account super -f Super -l User -p 1234 ./tool.sh set-user-admin super true -./tool.sh assign-workspace user1 sanity-ws -./tool.sh assign-workspace user2 sanity-ws -./tool.sh set-user-role user1 sanity-ws OWNER -./tool.sh set-user-role user2 sanity-ws OWNER # Make user the workspace maintainer ./tool.sh confirm-email user1 ./tool.sh confirm-email user2