fix(server): gate workspaceDiscoverability behind upgrade decorator (#22818)

## Context

Needs to be patched on 2.20, will craft a 2.19 equivalent with fallback
asap ( be it won't be merged unlike this one )

Fixes #22662. Follow-up to #22423, which introduced
`workspaceDiscoverability`.

A clean 2.18.x to 2.19 upgrade breaks on login with:

```
QueryFailedError: column workspaceDiscoverability does not exist
```

`workspaceDiscoverability` was added to `WorkspaceEntity` (in #22423) as
a plain, always-selected, non-nullable column, so any workspace query
(including the auth-path `findAvailableWorkspacesByEmail` lookup) fails
as soon as the ORM selects it, before the `2.19` upgrade command that
creates the column has run. Because the Docker entrypoint is fail-open,
the API starts even if the upgrade is delayed, and users hit this on
their first login.

## Changes

- Add `@WasIntroducedInUpgrade` to `workspaceDiscoverability`,
referencing the existing `2.19.0` fast instance command that creates the
column. The upgrade-aware ORM then skips the column until the command
has actually added it, keeping login working during the upgrade.
- Keep the GraphQL `@Field` non-nullable and add a
`workspaceDiscoverability` `@ResolveField` that falls back to
`WorkspaceDiscoverability.PUBLIC` while the column is hidden, so the
resolver never returns `null` for the non-nullable field during the
upgrade window.

This mirrors the existing pattern already applied to `FileEntity.status`
and `FileEntity.applicationRegistrationId`, and the resolver-default
pattern already used for `fastModel` / `smartModel` / `logo`.

## Cherry-pick

This fix needs to be cherry-picked onto both the **2.19** and **2.20**
release branches, since affected instances are upgrading into those
versions.

## Test

- `validate-upgrade-aware-entity-decorators` and
`resolve-entity-shape-at-upgrade-cursor` unit tests pass (the referenced
upgrade command name resolves correctly).
- `upgrade-aware-repository.proxy` and
`upgrade-aware-entity-metadata.adapter` specs pass.
- `typecheck` and lint pass for `twenty-server` and `twenty-front`.
- Regenerating the GraphQL schemas produces no diff (the field stays
non-nullable).
This commit is contained in:
Paul Rastoin
2026-07-13 14:20:28 +02:00
committed by GitHub
parent 8e66411203
commit 1b168ac1f7
2 changed files with 10 additions and 0 deletions
@@ -0,0 +1,4 @@
// Referenced by @WasIntroducedInUpgrade on the workspace "workspaceDiscoverability"
// column so pre-2.19 upgrade steps don't SELECT it before this command adds it.
export const ADD_WORKSPACE_DISCOVERABILITY_TO_WORKSPACE_UPGRADE_COMMAND_NAME =
'2.19.0_AddWorkspaceDiscoverabilityToWorkspaceFastInstanceCommand_1783004140000';
@@ -18,6 +18,7 @@ import {
UpdateDateColumn,
} from 'typeorm';
import { ADD_WORKSPACE_DISCOVERABILITY_TO_WORKSPACE_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-19/add-workspace-discoverability-to-workspace-upgrade-command-name.constant';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { ApiKeyEntity } from 'src/engine/core-modules/api-key/api-key.entity';
import { AppTokenEntity } from 'src/engine/core-modules/app-token/app-token.entity';
@@ -30,6 +31,7 @@ import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
import { KeyValuePairEntity } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity';
import { PublicDomainEntity } from 'src/engine/core-modules/public-domain/public-domain.entity';
import { WorkspaceSSOIdentityProviderEntity } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
import { WasIntroducedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-introduced-in-upgrade.decorator';
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { WorkspaceDiscoverability } from 'src/engine/core-modules/workspace/types/workspace-discoverability.type';
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
@@ -118,6 +120,10 @@ export class WorkspaceEntity {
isPublicInviteLinkEnabled: boolean;
@Field(() => WorkspaceDiscoverability)
@WasIntroducedInUpgrade({
upgradeCommandName:
ADD_WORKSPACE_DISCOVERABILITY_TO_WORKSPACE_UPGRADE_COMMAND_NAME,
})
@Column({
type: 'enum',
enumName: 'workspace_discoverability_enum',