diff --git a/models/task/src/migration.ts b/models/task/src/migration.ts index 1c674e1605..b2549a0c20 100644 --- a/models/task/src/migration.ts +++ b/models/task/src/migration.ts @@ -203,7 +203,7 @@ async function migrateProjectTypes (client: MigrationClient): Promise { modifiedOn: pt.modifiedOn, createdBy: pt.createdBy, createdOn: pt.createdOn, - modifiedBy: pt.modifiedBy + modifiedBy: pt.modifiedBy !== core.account.System ? pt.modifiedBy : core.account.ConfigUser } await client.create(DOMAIN_TX, tx) @@ -241,7 +241,7 @@ async function migrateTaskTypes (client: MigrationClient): Promise { modifiedOn: tt.modifiedOn, createdBy: tt.createdBy, createdOn: tt.createdOn, - modifiedBy: tt.modifiedBy + modifiedBy: tt.modifiedBy !== core.account.System ? tt.modifiedBy : core.account.ConfigUser } await client.create(DOMAIN_TX, tx) diff --git a/models/tracker/src/migration.ts b/models/tracker/src/migration.ts index 016f0132a8..ff0966f503 100644 --- a/models/tracker/src/migration.ts +++ b/models/tracker/src/migration.ts @@ -24,7 +24,9 @@ import core, { type Ref, type Status, type Tx, - type TxCreateDoc + type TxCreateDoc, + type StatusCategory, + type TxMixin } from '@hcengineering/core' import { createOrUpdate, @@ -365,6 +367,133 @@ async function migrateIdentifiers (client: MigrationClient): Promise { } } +async function restoreTaskTypes (client: MigrationClient): Promise { + // Query all tracker project types creations (in Model) + // We only update new project types in model here and not old ones in spaces + const projectTypes = (await client.find(DOMAIN_TX, { + _class: core.class.TxCreateDoc, + objectClass: task.class.ProjectType, + objectSpace: core.space.Model, + 'attributes.descriptor': tracker.descriptors.ProjectType + })) as TxCreateDoc[] + + if (projectTypes.length === 0) { + return + } + + const descr = client.model.getObject(tracker.descriptors.ProjectType) + const knownCategories = classicIssueTaskStatuses.map((c) => c.category) + + function compareCategories (a: Ref, b: Ref): number { + const indexOfA = knownCategories.indexOf(a) + const indexOfB = knownCategories.indexOf(b) + + return indexOfA - indexOfB + } + + for (const projType of projectTypes) { + for (const taskTypeId of projType.attributes.tasks) { + // Check if task type create TX exists + const createTx = ( + (await client.find(DOMAIN_TX, { + _class: core.class.TxCreateDoc, + objectClass: task.class.TaskType, + objectSpace: core.space.Model, + objectId: taskTypeId + })) as TxCreateDoc[] + )[0] + + if (createTx !== undefined) { + continue + } + + // Restore create task type tx + + // Get target class mixin + + const typeMixin = ( + await client.find(DOMAIN_TX, { + mixin: task.mixin.TaskTypeClass, + 'attributes.projectType': projType.objectId, + 'attributes.taskType': taskTypeId + }) + )[0] as TxMixin + + if (typeMixin === undefined) { + throw new Error('No type mixin found for the task type being restored') + } + + // Get statuses and categories + const statusesIds = projType.attributes.statuses.filter((s) => s.taskType === taskTypeId).map((s) => s._id) + if (statusesIds.length === 0) { + throw new Error('No statuses defined for the task type being restored') + } + const statuses = await client.find(DOMAIN_STATUS, { + _id: { $in: statusesIds } + }) + const categoriesIds = new Set>() + + statuses.forEach((st) => { + if (st.category !== undefined) { + categoriesIds.add(st.category) + } + }) + + if (categoriesIds.size === 0) { + throw new Error('No categories found for the task type being restored') + } + + const statusCategories = Array.from(categoriesIds) + + statusCategories.sort(compareCategories) + + const createTxNew: TxCreateDoc = { + _id: generateId(), + _class: core.class.TxCreateDoc, + space: core.space.Tx, + objectId: taskTypeId, + objectClass: task.class.TaskType, + objectSpace: core.space.Model, + modifiedBy: core.account.ConfigUser, // So it's not removed during the next migration + modifiedOn: projType.modifiedOn, + createdOn: projType.createdOn, + attributes: { + name: 'Issue', + descriptor: tracker.descriptors.Issue, + ofClass: tracker.class.Issue, + targetClass: typeMixin.objectId, + statusClass: tracker.class.IssueStatus, + allowedAsChildOf: [taskTypeId], + statuses: statusesIds, + statusCategories, + parent: projType.objectId, + kind: 'both', + icon: descr.icon + } + } + + await client.create(DOMAIN_TX, createTxNew) + + // If there were updates to the task type - move them to the model + // Check if task type create TX exists + const updateTxes = await client.find(DOMAIN_TX, { + _class: { $in: [core.class.TxUpdateDoc, core.class.TxRemoveDoc] }, + objectClass: task.class.TaskType, + objectSpace: projType.objectId, + objectId: taskTypeId + }) + + for (const updTx of updateTxes) { + await client.create(DOMAIN_TX, { + ...updTx, + _id: generateId(), + objectSpace: core.space.Model + }) + } + } + } +} + export const trackerOperation: MigrateOperation = { async migrate (client: MigrationClient): Promise { await tryMigrate(client, 'tracker', [ @@ -504,6 +633,10 @@ export const trackerOperation: MigrateOperation = { { state: 'passIdentifierToParentInfo', func: passIdentifierToParentInfo + }, + { + state: 'restoreTaskTypes', + func: restoreTaskTypes } ]) },