@@ -7,8 +7,12 @@ import { WorkspaceIteratorService } from 'src/database/commands/command-runners/
77import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner' ;
88import { getStandardFlatEntitiesToCreateOrThrow } from 'src/database/commands/upgrade-version-command/2-10/utils/get-standard-flat-entities-to-create-or-throw.util' ;
99import { buildDuplicateActivityTargetQuery } from 'src/database/commands/upgrade-version-command/2-38/utils/build-duplicate-activity-target-query.util' ;
10+ import { rebindFlatIndexToWorkspaceColumns } from 'src/database/commands/upgrade-version-command/2-38/utils/rebind-flat-index-to-workspace-columns.util' ;
11+ import { resolveActivityTargetColumns } from 'src/database/commands/upgrade-version-command/2-38/utils/resolve-activity-target-columns.util' ;
1012import { ApplicationService } from 'src/engine/core-modules/application/application.service' ;
1113import { RegisteredWorkspaceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-workspace-command.decorator' ;
14+ import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util' ;
15+ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type' ;
1216import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type' ;
1317import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service' ;
1418import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util' ;
@@ -17,23 +21,32 @@ import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager
1721import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service' ;
1822
1923const ACTIVITY_TARGET_CONFIGS = [
20- { tableName : 'taskTarget' , parentColumnName : 'taskId' } ,
21- { tableName : 'noteTarget' , parentColumnName : 'noteId' } ,
24+ {
25+ tableName : 'taskTarget' ,
26+ parentColumnName : 'taskId' ,
27+ uniqueIndexUniversalIdentifiers : [
28+ STANDARD_OBJECTS . taskTarget . indexes . taskPersonUniqueIndex
29+ . universalIdentifier ,
30+ STANDARD_OBJECTS . taskTarget . indexes . taskCompanyUniqueIndex
31+ . universalIdentifier ,
32+ STANDARD_OBJECTS . taskTarget . indexes . taskOpportunityUniqueIndex
33+ . universalIdentifier ,
34+ ] ,
35+ } ,
36+ {
37+ tableName : 'noteTarget' ,
38+ parentColumnName : 'noteId' ,
39+ uniqueIndexUniversalIdentifiers : [
40+ STANDARD_OBJECTS . noteTarget . indexes . notePersonUniqueIndex
41+ . universalIdentifier ,
42+ STANDARD_OBJECTS . noteTarget . indexes . noteCompanyUniqueIndex
43+ . universalIdentifier ,
44+ STANDARD_OBJECTS . noteTarget . indexes . noteOpportunityUniqueIndex
45+ . universalIdentifier ,
46+ ] ,
47+ } ,
2248] as const ;
2349
24- const ACTIVITY_TARGET_UNIQUE_INDEX_UNIVERSAL_IDENTIFIERS = [
25- STANDARD_OBJECTS . taskTarget . indexes . taskPersonUniqueIndex . universalIdentifier ,
26- STANDARD_OBJECTS . taskTarget . indexes . taskCompanyUniqueIndex
27- . universalIdentifier ,
28- STANDARD_OBJECTS . taskTarget . indexes . taskOpportunityUniqueIndex
29- . universalIdentifier ,
30- STANDARD_OBJECTS . noteTarget . indexes . notePersonUniqueIndex . universalIdentifier ,
31- STANDARD_OBJECTS . noteTarget . indexes . noteCompanyUniqueIndex
32- . universalIdentifier ,
33- STANDARD_OBJECTS . noteTarget . indexes . noteOpportunityUniqueIndex
34- . universalIdentifier ,
35- ] ;
36-
3750@RegisteredWorkspaceCommand ( '2.38.0' , 1788425677783 )
3851@Command ( {
3952 name : 'upgrade:2-38:enforce-activity-target-uniqueness' ,
@@ -85,23 +98,63 @@ export class EnforceActivityTargetUniquenessCommand extends ProvisionedWorkspace
8598 }
8699
87100 let duplicateCount = 0 ;
101+ const uniqueIndexUniversalIdentifiers : string [ ] = [ ] ;
102+ const columnNamesByIndexUniversalIdentifier = new Map < string , string [ ] > ( ) ;
88103
89104 for ( const targetConfig of ACTIVITY_TARGET_CONFIGS ) {
105+ const rows = await dataSource . query < Array < { column_name : string } > > (
106+ `SELECT column_name FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2` ,
107+ [ schemaName , targetConfig . tableName ] ,
108+ ) ;
109+ const targetColumns = resolveActivityTargetColumns (
110+ new Set ( rows . map ( ( { column_name } ) => column_name ) ) ,
111+ ) ;
112+
113+ if ( ! isDefined ( targetColumns ) ) {
114+ this . logger . error (
115+ `MANUAL REPAIR REQUIRED: skipping activity target uniqueness for ${ targetConfig . tableName } in workspace ${ workspaceId } : expected target columns are incomplete` ,
116+ ) ;
117+
118+ continue ;
119+ }
120+
90121 const [ result ] = await dataSource . query < Array < { count : number } > > (
91122 buildDuplicateActivityTargetQuery ( {
92123 schemaName,
93- ...targetConfig ,
124+ tableName : targetConfig . tableName ,
125+ parentColumnName : targetConfig . parentColumnName ,
126+ targetColumns,
94127 deleteDuplicates : ! options . dryRun ,
95128 } ) ,
96129 ) ;
97130
98131 duplicateCount += result ?. count ?? 0 ;
132+ uniqueIndexUniversalIdentifiers . push (
133+ ...targetConfig . uniqueIndexUniversalIdentifiers ,
134+ ) ;
135+ targetColumns . forEach ( ( targetColumn , index ) => {
136+ const uniqueIndexUniversalIdentifier =
137+ targetConfig . uniqueIndexUniversalIdentifiers [ index ] ;
138+
139+ if ( ! isDefined ( uniqueIndexUniversalIdentifier ) ) {
140+ throw new Error (
141+ `Could not resolve activity target index at position ${ index } ` ,
142+ ) ;
143+ }
144+
145+ columnNamesByIndexUniversalIdentifier . set (
146+ uniqueIndexUniversalIdentifier ,
147+ [ targetConfig . parentColumnName , targetColumn . columnName ] ,
148+ ) ;
149+ } ) ;
99150 }
100151
101- const { flatIndexMaps } = await this . workspaceCacheService . getOrRecompute (
102- workspaceId ,
103- [ 'flatIndexMaps' ] ,
104- ) ;
152+ const { flatIndexMaps, flatFieldMetadataMaps, flatObjectMetadataMaps } =
153+ await this . workspaceCacheService . getOrRecompute ( workspaceId , [
154+ 'flatIndexMaps' ,
155+ 'flatFieldMetadataMaps' ,
156+ 'flatObjectMetadataMaps' ,
157+ ] ) ;
105158 const { twentyStandardFlatApplication } =
106159 await this . applicationService . findWorkspaceTwentyStandardAndCustomApplicationOrThrow (
107160 { workspaceId } ,
@@ -112,13 +165,43 @@ export class EnforceActivityTargetUniquenessCommand extends ProvisionedWorkspace
112165 workspaceId,
113166 twentyStandardApplicationId : twentyStandardFlatApplication . id ,
114167 } ) ;
115- const indexesToCreate =
168+ const standardIndexesToCreate =
116169 getStandardFlatEntitiesToCreateOrThrow < FlatIndexMetadata > ( {
117170 standardFlatEntityMaps : standardAllFlatEntityMaps . flatIndexMaps ,
118171 existingFlatEntityMaps : flatIndexMaps ,
119- universalIdentifiers :
120- ACTIVITY_TARGET_UNIQUE_INDEX_UNIVERSAL_IDENTIFIERS ,
172+ universalIdentifiers : uniqueIndexUniversalIdentifiers ,
121173 } ) ;
174+ const indexesToCreate = standardIndexesToCreate . map (
175+ ( standardFlatIndexMetadata ) => {
176+ const flatObjectMetadata = findFlatEntityByUniversalIdentifierOrThrow ( {
177+ universalIdentifier :
178+ standardFlatIndexMetadata . objectMetadataUniversalIdentifier ,
179+ flatEntityMaps : flatObjectMetadataMaps ,
180+ } ) ;
181+ const columnNames = columnNamesByIndexUniversalIdentifier . get (
182+ standardFlatIndexMetadata . universalIdentifier ,
183+ ) ;
184+
185+ if ( ! isDefined ( columnNames ) ) {
186+ throw new Error (
187+ `Could not resolve columns for activity target index ${ standardFlatIndexMetadata . universalIdentifier } ` ,
188+ ) ;
189+ }
190+
191+ return rebindFlatIndexToWorkspaceColumns ( {
192+ flatIndexMetadata : standardFlatIndexMetadata ,
193+ flatObjectMetadata,
194+ objectFlatFieldMetadatas : Object . values (
195+ flatFieldMetadataMaps . byUniversalIdentifier ,
196+ ) . filter (
197+ ( flatFieldMetadata ) : flatFieldMetadata is FlatFieldMetadata =>
198+ isDefined ( flatFieldMetadata ) &&
199+ flatFieldMetadata . objectMetadataId === flatObjectMetadata . id ,
200+ ) ,
201+ columnNames,
202+ } ) ;
203+ } ,
204+ ) ;
122205
123206 this . logger . log (
124207 `${ options . dryRun ? '[DRY RUN] Would remove' : 'Removed' } ${ duplicateCount } duplicate activity target(s) and ${ options . dryRun ? 'would create' : 'will create' } ${ indexesToCreate . length } unique index(es) for workspace ${ workspaceId } ` ,
0 commit comments