fix(integration-ever-works): the connection's organization relation is ON DELETE SET NULL in the entity too

Merged
EV
7d ago
+116-3

Fixes the Greptile finding deferred on the develop → stage cascade #11096 (thread): Organization deletion still removes connections.

Problem

Migration 1791000003100 sets ever_works_connection.organizationId to ON DELETE SET NULL, so a tenant's connection survives the deletion of the organization it was connected from (review F59). The entity, however, inherited TenantOrganizationBaseEntity's organization relation with onDelete: 'CASCADE'. A schema built with DB_SYNCHRONIZE=true therefore deleted the connection, which stopped every other mapping of the tenant.

Overriding the relation in the subclass does not work. TypeORM's MetadataArgsStorage.filterByTargetAndWithoutDuplicateRelationProperties keeps the base relation's options and copies only the subclass's type onto them. #11119 tried this and reverted it.

Fix (connection/ever-works-connection.entity.ts)

EverWorksConnection now extends TenantBaseEntity and declares its own:

  • organization: @MultiORMManyToOne(() => Organization, { nullable: true, onUpdate: 'CASCADE', onDelete: 'SET NULL' });
  • organizationId: the base's decorators verbatim (ApiPropertyOptional, IsOptional, IsUUID, RelationId, ColumnIndex, MultiORMColumn({ nullable: true, relationId: true })).

It still implements IBasePerTenantAndOrganizationEntityModel. The same pattern exists in auth-zitadel (ZitadelSession extends TenantBaseEntity with its own relation).

What else depends on the base class:

  • Generics / type constraints: none. No TenantOrganizationAwareCrudService, and MikroOrmBaseEntityRepository<T extends object>. tsc -p tsconfig.lib.json reports 0 errors in the plugin.
  • Core instanceof checks: only TenantOrganizationBaseEntityEventSubscriber. It sets organization = { id: organizationId } on create, which MikroORM needs because organizationId is persist: false there. Every connection write in the plugin goes through the TypeORM DataSource, which writes the organizationId column directly. The tenant part of the subscriber still applies.
  • Export/import: reads the decorators, not the class. Core export-redact.entities.spec.ts passes 20/20.
  • Reads of connection.organization: none (organizationId only).

Proof that only the delete rule changes

A throwaway spec (not committed) built core's entities and the six Ever Works entities on SQLite with synchronize: true, the DB_SYNCHRONIZE=true path. I ran it on develop's entity and on this one, then diffed PRAGMA foreign_key_list, table_info and the indexes of ever_works_connection. The only difference is the organization foreign key's on_delete: CASCADE → SET NULL. Columns, column order, index names (IDX_86d6739e… on organizationId) and the tenant FK (CASCADE) are identical, so a migrated database sees no new diff.

Tests

  • New connection/ever-works-connection.entity.spec.ts reads the real TypeORM metadata (real @gauzy/core decorators, no double):
    • organization is nullable, onUpdate: CASCADE, onDelete: SET NULL. This fails on develop (CASCADE) and passes here.
    • The entity is still a TenantBaseEntity, tenant stays CASCADE, and organizationId keeps its nullable column, relation id and index.
  • The plugin's specs double @gauzy/core. Their doubles gain TenantBaseEntity (and MultiORMManyToOne where missing): one or two lines in 24 spec files, test-only.
  • Locally (Node 26, --experimental-vm-modules): plugin 45/45 suites, 817/817 tests; core export-redact.entities.spec.ts 20/20.

Additive only: no migration (1791000003100 already did the database side), and no column, index or API change.

šŸ¤– Generated with Claude Code


Summary by cubic

Fixes the Ever Works connection entity so its organization relation is ON DELETE SET NULL, matching migration 1791000003100 and preventing connection loss when an organization is deleted on schemas built with DB_SYNCHRONIZE=true.

  • EverWorksConnection now extends TenantBaseEntity and declares its own nullable organization relation (ON DELETE SET NULL, ON UPDATE CASCADE) with the appropriate organizationId column decorators; overriding the inherited relation in a subclass doesn't work in TypeORM.
  • Adds a new spec that validates the real TypeORM metadata for the relation options correspond to the migration.
  • Updates 24 plugin spec files' @gauzy/core doubles to include TenantBaseEntity and MultiORMManyToOne; test-only, no behavior change.

No migration or column/index/API changes; entity behavior and schema output now match the existing migration.