common api - null equivalence (#15926)

closes https://github.com/twentyhq/core-team-issues/issues/1629

To do before requesting review : 
- filter update

Migration to come in an other PR


Strat : 
1/  Null transformation

- [x] Transform NULL equivalent value to NULL in field validation in
common api - pre-query - with feature flag
- [ ] Same logic in ORM (Not done, complex to handle feature flag here)
- [x] Transform NULL value to equivalent in data formatting in ORM -
post-query

2/ Migration (in other PR) for fieldMetadata not nullable with default
defaultValue (empty string, ...)

- [ ] Remove NOT NULL db constraint
- [ ] Update record value to NULL
- [ ] Update field metadata : isNullable:true
- [ ] Update uniqueIndex whereClause (also for standard uniqueIndex) 
- [ ] Activate feature flag

3/ Update metadata creation

- [x] No more default default value
- [x] Update standard field nullability
- [x] Remove index default whereClause for standard field

4/ Update filter

- [x] When filtering on NULL or empty string, be sure all records are
returned (the one with NULL + the one with "")

5/ Test

- [ ] Strat. to do
This commit is contained in:
Etienne
2025-11-24 18:57:47 +01:00
committed by GitHub
parent 3ed6d2a16b
commit 208c0857ee
59 changed files with 884 additions and 300 deletions
@@ -7,8 +7,7 @@ import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/
import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { type ObjectPermissions } from 'twenty-shared/types';
import { capitalize } from 'twenty-shared/utils';
import { isEmptyObject } from '~/utils/isEmptyObject';
import { capitalize, isEmptyObject } from 'twenty-shared/utils';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
export type GetRecordFromCacheArgs = {
@@ -40,9 +40,8 @@ import {
type TSVectorFilter,
type UUIDFilter,
} from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { isDefined, isEmptyObject } from 'twenty-shared/utils';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isEmptyObject } from '~/utils/isEmptyObject';
const isLeafFilter = (
filter: RecordGqlOperationFilter,
@@ -8,12 +8,15 @@ import {
} from '@/spreadsheet-import/types';
import { isNonEmptyString } from '@sniptt/guards';
import { parsePhoneNumberWithError, type CountryCode } from 'libphonenumber-js';
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
import {
assertUnreachable,
isDefined,
isEmptyObject,
} from 'twenty-shared/utils';
import { z } from 'zod';
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
import { castToString } from '~/utils/castToString';
import { convertCurrencyAmountToCurrencyMicros } from '~/utils/convertCurrencyToCurrencyMicros';
import { isEmptyObject } from '~/utils/isEmptyObject';
import { stripSimpleQuotesFromString } from '~/utils/string/stripSimpleQuotesFromString';
type BuildRecordFromImportedStructuredRowArgs = {