Skip to content

Conversation

etiennejouan
Copy link
Contributor

@etiennejouan etiennejouan commented Sep 17, 2025

Done :

  • migrate standard unique fields (add : -old)
  • deactivate defaultWhereClause
  • restore soft deleted if upserted (same for contact creation in mailing sync)

fixes #14443

Copy link
Contributor

github-actions bot commented Sep 17, 2025

🚀 Preview Environment Ready!

Your preview environment is available at: http://bore.pub:11057

This environment will automatically shut down when the PR is closed or after 5 hours.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Summary

This PR fixes a critical issue with CSV uploads when mapping multiple unique identifiers by updating the unique field constraint logic to include soft-deleted records.

Key Changes:

  • Database indexes: Removed "deletedAt" IS NULL constraint from unique indexes so soft-deleted records are included in uniqueness checks
  • Upsert logic: Enhanced GraphQL resolver to restore soft-deleted records by setting deletedAt: null during upserts
  • Contact/company creation: Added restoration logic for soft-deleted contacts and companies instead of treating them as conflicts
  • Database migration: Added v1.7 upgrade command to deduplicate existing unique field violations
  • Testing: Comprehensive integration tests covering soft-delete restoration scenarios

The changes ensure that when users upload CSV data with existing unique identifiers from soft-deleted records, those records are restored rather than causing constraint violations.

Confidence Score: 4/5

  • This PR is generally safe to merge with minor syntax issues to address
  • Score reflects solid implementation of soft-delete restoration logic with comprehensive testing, but contains syntax errors that should be fixed before merge
  • packages/twenty-server/src/database/commands/upgrade-version-command/1-7/1-7-deduplicate-unique-fields.command.ts needs syntax fixes

Important Files Changed

File Analysis

Filename        Score        Overview
packages/twenty-server/src/engine/workspace-manager/workspace-migration-builder/factories/utils/workspace-migration-index.factory.utils.ts 5/5 Removed "deletedAt" IS NULL constraint from unique indexes to include soft-deleted records in uniqueness checks
packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-many-resolver.service.ts 4/5 Enhanced upsert logic to restore soft-deleted records by setting deletedAt: null and updated query to include soft-deleted records
packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts 4/5 Added restoration logic for soft-deleted companies, including filtering and updating deleted companies with deletedAt: null
packages/twenty-server/src/database/commands/upgrade-version-command/1-7/1-7-deduplicate-unique-fields.command.ts 3/5 New command to deduplicate unique fields for existing data, handling workspace members, companies and people with duplicate constraints

Sequence Diagram

sequenceDiagram
    participant U as User
    participant API as GraphQL API  
    participant R as CreateManyResolver
    participant DB as Database
    participant CS as ContactService
    participant COS as CompanyService

    U->>API: CSV Upload with unique identifiers
    API->>R: createMany with upsert: true
    
    R->>DB: Query existing records (including soft-deleted)
    DB->>R: Return existing records
    
    alt Record exists and is soft-deleted
        R->>DB: Update record with deletedAt: null
        DB->>R: Restored record
    else Record exists and not deleted
        R->>DB: Update existing record
        DB->>R: Updated record
    else Record doesn't exist
        R->>DB: Insert new record
        DB->>R: New record
    end

    alt Contact creation workflow
        CS->>DB: Find soft-deleted contacts by email
        DB->>CS: Soft-deleted contacts
        CS->>COS: Create/restore companies for contacts
        COS->>DB: Restore soft-deleted companies
        DB->>COS: Restored companies
        CS->>DB: Restore contacts with company associations
        DB->>CS: Restored contacts
    end

    R->>API: Return processed records
    API->>U: Success response
Loading

15 files reviewed, 4 comments

Edit Code Review Bot Settings | Greptile

constructor(
@InjectRepository(Workspace)
protected readonly workspaceRepository: Repository<Workspace>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runOnWorkspace is already providing an ORM Manager that is managed by the command itself (handle injection and destruction

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually I'm wrong here!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's the way an upgrade command should be instantiated


return this.createContactService.createPeople(
const restoredContacts =
await this.restorePeopleAndRestoreOrCreateCompanies(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have kept one function that's doing both

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cannot upload a csv mapping 2 unique identifiers
2 participants