Null equivalence - remove feature flag (#16222)
This commit is contained in:
@@ -1287,7 +1287,6 @@ export enum FeatureFlagKey {
|
||||
IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED = 'IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED',
|
||||
IS_IMAP_SMTP_CALDAV_ENABLED = 'IS_IMAP_SMTP_CALDAV_ENABLED',
|
||||
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
|
||||
IS_NULL_EQUIVALENCE_ENABLED = 'IS_NULL_EQUIVALENCE_ENABLED',
|
||||
IS_PAGE_LAYOUT_ENABLED = 'IS_PAGE_LAYOUT_ENABLED',
|
||||
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
|
||||
IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED',
|
||||
|
||||
@@ -1270,7 +1270,6 @@ export enum FeatureFlagKey {
|
||||
IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED = 'IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED',
|
||||
IS_IMAP_SMTP_CALDAV_ENABLED = 'IS_IMAP_SMTP_CALDAV_ENABLED',
|
||||
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
|
||||
IS_NULL_EQUIVALENCE_ENABLED = 'IS_NULL_EQUIVALENCE_ENABLED',
|
||||
IS_PAGE_LAYOUT_ENABLED = 'IS_PAGE_LAYOUT_ENABLED',
|
||||
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
|
||||
IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED',
|
||||
|
||||
-31
@@ -13,7 +13,6 @@ import { Repository } from 'typeorm';
|
||||
|
||||
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
|
||||
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
@@ -83,22 +82,6 @@ export class CleanNullEquivalentValuesCommand extends ActiveOrSuspendedWorkspace
|
||||
this.logger.log('Dry run mode: No changes will be applied');
|
||||
}
|
||||
|
||||
const featureFlag = await this.featureFlagRepository.findOne({
|
||||
where: {
|
||||
key: FeatureFlagKey.IS_NULL_EQUIVALENCE_ENABLED,
|
||||
value: true,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (isDefined(featureFlag)) {
|
||||
this.logger.log(
|
||||
`Feature flag ${FeatureFlagKey.IS_NULL_EQUIVALENCE_ENABLED} already enabled for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const objectMetadataItems = await this.objectMetadataRepository.find({
|
||||
where: { workspaceId },
|
||||
relations: [
|
||||
@@ -278,20 +261,6 @@ export class CleanNullEquivalentValuesCommand extends ActiveOrSuspendedWorkspace
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isDryRun) {
|
||||
await this.featureFlagRepository.upsert(
|
||||
{
|
||||
key: FeatureFlagKey.IS_NULL_EQUIVALENCE_ENABLED,
|
||||
value: true,
|
||||
workspaceId,
|
||||
},
|
||||
['key', 'workspaceId'],
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`Switched on feature flag ${FeatureFlagKey.IS_NULL_EQUIVALENCE_ENABLED} for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private objectEquals(obj1: unknown, obj2: unknown): boolean {
|
||||
|
||||
+8
-17
@@ -44,7 +44,6 @@ import {
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
|
||||
import { transformEmailsValue } from 'src/engine/core-modules/record-transformer/utils/transform-emails-value.util';
|
||||
@@ -85,12 +84,6 @@ export class DataArgProcessor {
|
||||
|
||||
assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError);
|
||||
|
||||
const isNullEquivalenceEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_NULL_EQUIVALENCE_ENABLED,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
const { fieldIdByName, fieldIdByJoinColumnName } =
|
||||
buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
@@ -141,7 +134,6 @@ export class DataArgProcessor {
|
||||
fieldMetadata,
|
||||
key,
|
||||
value,
|
||||
isNullEquivalenceEnabled,
|
||||
);
|
||||
}
|
||||
processedRecords.push(processedRecord);
|
||||
@@ -166,7 +158,6 @@ export class DataArgProcessor {
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
key: string,
|
||||
value: unknown,
|
||||
isNullEquivalenceEnabled: boolean,
|
||||
): Promise<unknown> {
|
||||
switch (fieldMetadata.type) {
|
||||
case FieldMetadataType.POSITION:
|
||||
@@ -182,7 +173,7 @@ export class DataArgProcessor {
|
||||
case FieldMetadataType.TEXT: {
|
||||
const validatedValue = validateTextFieldOrThrow(value, key);
|
||||
|
||||
return transformTextField(validatedValue, isNullEquivalenceEnabled);
|
||||
return transformTextField(validatedValue);
|
||||
}
|
||||
case FieldMetadataType.DATE_TIME:
|
||||
case FieldMetadataType.DATE:
|
||||
@@ -207,19 +198,19 @@ export class DataArgProcessor {
|
||||
fieldMetadata.options?.map((option) => option.value),
|
||||
);
|
||||
|
||||
return transformArrayField(validatedValue, isNullEquivalenceEnabled);
|
||||
return transformArrayField(validatedValue);
|
||||
}
|
||||
case FieldMetadataType.UUID:
|
||||
return validateUUIDFieldOrThrow(value, key);
|
||||
case FieldMetadataType.ARRAY: {
|
||||
const validatedValue = validateArrayFieldOrThrow(value, key);
|
||||
|
||||
return transformArrayField(validatedValue, isNullEquivalenceEnabled);
|
||||
return transformArrayField(validatedValue);
|
||||
}
|
||||
case FieldMetadataType.RAW_JSON: {
|
||||
const validatedValue = validateRawJsonFieldOrThrow(value, key);
|
||||
|
||||
return transformRawJsonField(validatedValue, isNullEquivalenceEnabled);
|
||||
return transformRawJsonField(validatedValue);
|
||||
}
|
||||
case FieldMetadataType.RELATION:
|
||||
case FieldMetadataType.MORPH_RELATION: {
|
||||
@@ -250,18 +241,18 @@ export class DataArgProcessor {
|
||||
case FieldMetadataType.EMAILS: {
|
||||
const validatedValue = validateEmailsFieldOrThrow(value, key);
|
||||
|
||||
return transformEmailsValue(validatedValue, isNullEquivalenceEnabled);
|
||||
return transformEmailsValue(validatedValue);
|
||||
}
|
||||
case FieldMetadataType.FULL_NAME: {
|
||||
const validatedValue = validateFullNameFieldOrThrow(value, key);
|
||||
|
||||
return transformFullNameField(validatedValue, isNullEquivalenceEnabled);
|
||||
return transformFullNameField(validatedValue);
|
||||
}
|
||||
|
||||
case FieldMetadataType.ADDRESS: {
|
||||
const validatedValue = validateAddressFieldOrThrow(value, key);
|
||||
|
||||
return transformAddressField(validatedValue, isNullEquivalenceEnabled);
|
||||
return transformAddressField(validatedValue);
|
||||
}
|
||||
case FieldMetadataType.CURRENCY: {
|
||||
const validatedValue = validateCurrencyFieldOrThrow(value, key);
|
||||
@@ -271,7 +262,7 @@ export class DataArgProcessor {
|
||||
case FieldMetadataType.ACTOR: {
|
||||
const validatedValue = validateActorFieldOrThrow(value, key);
|
||||
|
||||
return transformActorField(validatedValue, isNullEquivalenceEnabled);
|
||||
return transformActorField(validatedValue);
|
||||
}
|
||||
case FieldMetadataType.RICH_TEXT_V2: {
|
||||
const validatedValue = validateRichTextV2FieldOrThrow(value, key);
|
||||
|
||||
+20
-35
@@ -4,18 +4,15 @@ import { transformActorField } from 'src/engine/api/common/common-args-processor
|
||||
|
||||
describe('transformActorField', () => {
|
||||
it('should return null when value is null', () => {
|
||||
const result = transformActorField(null, true);
|
||||
const result = transformActorField(null);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should transform actor with source only', () => {
|
||||
const result = transformActorField(
|
||||
{
|
||||
source: FieldActorSource.EMAIL,
|
||||
},
|
||||
true,
|
||||
);
|
||||
const result = transformActorField({
|
||||
source: FieldActorSource.EMAIL,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
source: FieldActorSource.EMAIL,
|
||||
@@ -23,13 +20,10 @@ describe('transformActorField', () => {
|
||||
});
|
||||
|
||||
it('should transform actor with source and context', () => {
|
||||
const result = transformActorField(
|
||||
{
|
||||
source: FieldActorSource.WORKFLOW,
|
||||
context: { workflowId: '123', stepId: 'step-1' },
|
||||
},
|
||||
true,
|
||||
);
|
||||
const result = transformActorField({
|
||||
source: FieldActorSource.WORKFLOW,
|
||||
context: { workflowId: '123', stepId: 'step-1' },
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
source: FieldActorSource.WORKFLOW,
|
||||
@@ -38,13 +32,10 @@ describe('transformActorField', () => {
|
||||
});
|
||||
|
||||
it('should transform actor with null source', () => {
|
||||
const result = transformActorField(
|
||||
{
|
||||
source: null,
|
||||
context: { userId: '456' },
|
||||
},
|
||||
true,
|
||||
);
|
||||
const result = transformActorField({
|
||||
source: null,
|
||||
context: { userId: '456' },
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
source: null,
|
||||
@@ -53,13 +44,10 @@ describe('transformActorField', () => {
|
||||
});
|
||||
|
||||
it('should transform actor with null context', () => {
|
||||
const result = transformActorField(
|
||||
{
|
||||
source: FieldActorSource.API,
|
||||
context: null,
|
||||
},
|
||||
true,
|
||||
);
|
||||
const result = transformActorField({
|
||||
source: FieldActorSource.API,
|
||||
context: null,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
source: FieldActorSource.API,
|
||||
@@ -68,13 +56,10 @@ describe('transformActorField', () => {
|
||||
});
|
||||
|
||||
it('should transform empty context object to null', () => {
|
||||
const result = transformActorField(
|
||||
{
|
||||
source: FieldActorSource.EMAIL,
|
||||
context: {},
|
||||
},
|
||||
true,
|
||||
);
|
||||
const result = transformActorField({
|
||||
source: FieldActorSource.EMAIL,
|
||||
context: {},
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
source: FieldActorSource.EMAIL,
|
||||
|
||||
+4
-4
@@ -2,25 +2,25 @@ import { transformArrayField } from 'src/engine/api/common/common-args-processor
|
||||
|
||||
describe('transformArrayField', () => {
|
||||
it('should return null when value is null', () => {
|
||||
const result = transformArrayField(null, true);
|
||||
const result = transformArrayField(null);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should return null when value is an empty array', () => {
|
||||
const result = transformArrayField([], true);
|
||||
const result = transformArrayField([]);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should return an array when value is a string', () => {
|
||||
const result = transformArrayField('singleString', true);
|
||||
const result = transformArrayField('singleString');
|
||||
|
||||
expect(result).toEqual(['singleString']);
|
||||
});
|
||||
|
||||
it('should return an array when value is an array of strings', () => {
|
||||
const result = transformArrayField(['string1', 'string2', 'string3'], true);
|
||||
const result = transformArrayField(['string1', 'string2', 'string3']);
|
||||
|
||||
expect(result).toEqual(['string1', 'string2', 'string3']);
|
||||
});
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@ import { transformFullNameField } from 'src/engine/api/common/common-args-proces
|
||||
|
||||
describe('transformFullNameField', () => {
|
||||
it('should return null when value is null', () => {
|
||||
const result = transformFullNameField(null, true);
|
||||
const result = transformFullNameField(null);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
@@ -12,7 +12,7 @@ describe('transformFullNameField', () => {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
};
|
||||
const result = transformFullNameField(value, true);
|
||||
const result = transformFullNameField(value);
|
||||
|
||||
expect(result).toEqual({
|
||||
firstName: 'John',
|
||||
@@ -24,7 +24,7 @@ describe('transformFullNameField', () => {
|
||||
const value = {
|
||||
lastName: '',
|
||||
};
|
||||
const result = transformFullNameField(value, true);
|
||||
const result = transformFullNameField(value);
|
||||
|
||||
expect(result).toEqual({
|
||||
lastName: null,
|
||||
|
||||
+3
-3
@@ -2,19 +2,19 @@ import { transformRawJsonField } from 'src/engine/api/common/common-args-process
|
||||
|
||||
describe('transformRawJsonField', () => {
|
||||
it('should return null when value is null', () => {
|
||||
const result = transformRawJsonField(null, true);
|
||||
const result = transformRawJsonField(null);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should return null when value is empty object', () => {
|
||||
const result = transformRawJsonField({}, true);
|
||||
const result = transformRawJsonField({});
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should return the string when value is empty array', () => {
|
||||
const result = transformRawJsonField([], true);
|
||||
const result = transformRawJsonField([]);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
+3
-3
@@ -2,19 +2,19 @@ import { transformTextField } from 'src/engine/api/common/common-args-processors
|
||||
|
||||
describe('transformTextField', () => {
|
||||
it('should return null when value is null', () => {
|
||||
const result = transformTextField(null, true);
|
||||
const result = transformTextField(null);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should return null when value is empty string', () => {
|
||||
const result = transformTextField('', true);
|
||||
const result = transformTextField('');
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should return the string when value is a non-empty string', () => {
|
||||
const result = transformTextField('hello world', true);
|
||||
const result = transformTextField('hello world');
|
||||
|
||||
expect(result).toBe('hello world');
|
||||
});
|
||||
|
||||
+2
-5
@@ -11,7 +11,6 @@ export const transformActorField = (
|
||||
name?: string | null;
|
||||
workspaceMemberId?: string | null;
|
||||
} | null,
|
||||
isNullEquivalenceEnabled: boolean = false,
|
||||
): {
|
||||
source?: FieldActorSource | null;
|
||||
context?: object | string | null;
|
||||
@@ -24,10 +23,8 @@ export const transformActorField = (
|
||||
source: value.source,
|
||||
context: isUndefined(value.context)
|
||||
? undefined
|
||||
: transformRawJsonField(value.context, isNullEquivalenceEnabled),
|
||||
name: isUndefined(value.name)
|
||||
? undefined
|
||||
: transformTextField(value.name, isNullEquivalenceEnabled),
|
||||
: transformRawJsonField(value.context),
|
||||
name: isUndefined(value.name) ? undefined : transformTextField(value.name),
|
||||
workspaceMemberId: isUndefined(value.workspaceMemberId)
|
||||
? undefined
|
||||
: value.workspaceMemberId,
|
||||
|
||||
+6
-7
@@ -14,7 +14,6 @@ export const transformAddressField = (
|
||||
addressLat?: number | null;
|
||||
addressLng?: number | null;
|
||||
} | null,
|
||||
isNullEquivalenceEnabled: boolean = false,
|
||||
): {
|
||||
addressStreet1?: string | null;
|
||||
addressStreet2?: string | null;
|
||||
@@ -30,22 +29,22 @@ export const transformAddressField = (
|
||||
return {
|
||||
addressStreet1: isUndefined(value.addressStreet1)
|
||||
? undefined
|
||||
: transformTextField(value.addressStreet1, isNullEquivalenceEnabled),
|
||||
: transformTextField(value.addressStreet1),
|
||||
addressStreet2: isUndefined(value.addressStreet2)
|
||||
? undefined
|
||||
: transformTextField(value.addressStreet2, isNullEquivalenceEnabled),
|
||||
: transformTextField(value.addressStreet2),
|
||||
addressCity: isUndefined(value.addressCity)
|
||||
? undefined
|
||||
: transformTextField(value.addressCity, isNullEquivalenceEnabled),
|
||||
: transformTextField(value.addressCity),
|
||||
addressState: isUndefined(value.addressState)
|
||||
? undefined
|
||||
: transformTextField(value.addressState, isNullEquivalenceEnabled),
|
||||
: transformTextField(value.addressState),
|
||||
addressPostcode: isUndefined(value.addressPostcode)
|
||||
? undefined
|
||||
: transformTextField(value.addressPostcode, isNullEquivalenceEnabled),
|
||||
: transformTextField(value.addressPostcode),
|
||||
addressCountry: isUndefined(value.addressCountry)
|
||||
? undefined
|
||||
: transformTextField(value.addressCountry, isNullEquivalenceEnabled),
|
||||
: transformTextField(value.addressCountry),
|
||||
addressLat: isUndefined(value.addressLat)
|
||||
? undefined
|
||||
: transformNumericField(value.addressLat),
|
||||
|
||||
+1
-4
@@ -2,11 +2,8 @@ import { isNullEquivalentArrayFieldValue } from 'src/engine/api/common/common-ar
|
||||
|
||||
export const transformArrayField = (
|
||||
value: string | string[] | null,
|
||||
isNullEquivalenceEnabled: boolean = false,
|
||||
): string[] | null => {
|
||||
if (typeof value === 'string') return [value];
|
||||
|
||||
return isNullEquivalenceEnabled && isNullEquivalentArrayFieldValue(value)
|
||||
? null
|
||||
: value;
|
||||
return isNullEquivalentArrayFieldValue(value) ? null : value;
|
||||
};
|
||||
|
||||
+1
-2
@@ -8,7 +8,6 @@ export const transformCurrencyField = (
|
||||
amountMicros?: number | string | null;
|
||||
currencyCode?: string | null;
|
||||
} | null,
|
||||
isNullEquivalenceEnabled: boolean = false,
|
||||
): {
|
||||
amountMicros?: number | null;
|
||||
currencyCode?: string | null;
|
||||
@@ -21,6 +20,6 @@ export const transformCurrencyField = (
|
||||
: transformNumericField(value.amountMicros),
|
||||
currencyCode: isUndefined(value.currencyCode)
|
||||
? undefined
|
||||
: transformTextField(value.currencyCode, isNullEquivalenceEnabled),
|
||||
: transformTextField(value.currencyCode),
|
||||
};
|
||||
};
|
||||
|
||||
+2
-3
@@ -7,7 +7,6 @@ export const transformFullNameField = (
|
||||
firstName?: string | null;
|
||||
lastName?: string | null;
|
||||
} | null,
|
||||
isNullEquivalenceEnabled: boolean = false,
|
||||
): {
|
||||
firstName?: string | null;
|
||||
lastName?: string | null;
|
||||
@@ -17,9 +16,9 @@ export const transformFullNameField = (
|
||||
return {
|
||||
firstName: isUndefined(value.firstName)
|
||||
? undefined
|
||||
: transformTextField(value.firstName, isNullEquivalenceEnabled),
|
||||
: transformTextField(value.firstName),
|
||||
lastName: isUndefined(value.lastName)
|
||||
? undefined
|
||||
: transformTextField(value.lastName, isNullEquivalenceEnabled),
|
||||
: transformTextField(value.lastName),
|
||||
};
|
||||
};
|
||||
|
||||
+1
-4
@@ -3,9 +3,6 @@ import { isNullEquivalentRawJsonFieldValue } from 'src/engine/api/common/common-
|
||||
|
||||
export const transformRawJsonField = (
|
||||
value: object | string | null,
|
||||
isNullEquivalenceEnabled: boolean = false,
|
||||
): object | string | null => {
|
||||
return isNullEquivalenceEnabled && isNullEquivalentRawJsonFieldValue(value)
|
||||
? null
|
||||
: value;
|
||||
return isNullEquivalentRawJsonFieldValue(value) ? null : value;
|
||||
};
|
||||
|
||||
+2
-7
@@ -1,10 +1,5 @@
|
||||
import { isNullEquivalentTextFieldValue } from 'src/engine/api/common/common-args-processors/data-arg-processor/utils/is-null-equivalent-text-field-value.util';
|
||||
|
||||
export const transformTextField = (
|
||||
value: string | null,
|
||||
isNullEquivalenceEnabled: boolean = false,
|
||||
): string | null => {
|
||||
return isNullEquivalenceEnabled && isNullEquivalentTextFieldValue(value)
|
||||
? null
|
||||
: value;
|
||||
export const transformTextField = (value: string | null): string | null => {
|
||||
return isNullEquivalentTextFieldValue(value) ? null : value;
|
||||
};
|
||||
|
||||
-1
@@ -15,5 +15,4 @@ export enum FeatureFlagKey {
|
||||
IS_WORKFLOW_RUN_STOPPAGE_ENABLED = 'IS_WORKFLOW_RUN_STOPPAGE_ENABLED',
|
||||
IS_DASHBOARD_V2_ENABLED = 'IS_DASHBOARD_V2_ENABLED',
|
||||
IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED = 'IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED',
|
||||
IS_NULL_EQUIVALENCE_ENABLED = 'IS_NULL_EQUIVALENCE_ENABLED',
|
||||
}
|
||||
|
||||
-16
@@ -16,7 +16,6 @@ import { getCompositeTypeOrThrow } from 'src/engine/metadata-modules/field-metad
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { computeUniqueIndexWhereClause } from 'src/engine/metadata-modules/index-metadata/utils/compute-unique-index-where-clause.util';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
@Injectable()
|
||||
@@ -148,17 +147,6 @@ export class UpsertRecordService {
|
||||
? conflictPathsUniqueFieldsToUpdate
|
||||
: ['id'];
|
||||
|
||||
//TODO : To delete once IS_NULL_EQUIVALENCE_ENABLED feature flag removed
|
||||
const indexPredicate = uniqueFieldsToUpdate
|
||||
.map((field) =>
|
||||
computeUniqueIndexWhereClause({
|
||||
type: field.type,
|
||||
name: field.name,
|
||||
defaultValue: field.defaultValue,
|
||||
}),
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
const restrictedFields =
|
||||
repository.objectRecordsPermissions?.[flatObjectMetadata.id]
|
||||
?.restrictedFields;
|
||||
@@ -173,10 +161,6 @@ export class UpsertRecordService {
|
||||
transformedObjectRecord,
|
||||
{
|
||||
conflictPaths: conflictPaths,
|
||||
indexPredicate:
|
||||
indexPredicate.length > 0
|
||||
? `${indexPredicate.join(' AND ')}`
|
||||
: undefined,
|
||||
},
|
||||
undefined,
|
||||
selectedColumns,
|
||||
|
||||
+5
-15
@@ -1,9 +1,8 @@
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
export const transformEmailsValue = (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any,
|
||||
isNullEquivalenceEnabled: boolean = false,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
): any => {
|
||||
if (!value) {
|
||||
@@ -13,9 +12,7 @@ export const transformEmailsValue = (
|
||||
let additionalEmails: string | null = value?.additionalEmails;
|
||||
const primaryEmail = value?.primaryEmail
|
||||
? value.primaryEmail.toLowerCase()
|
||||
: isNullEquivalenceEnabled
|
||||
? null
|
||||
: '';
|
||||
: null;
|
||||
|
||||
if (additionalEmails) {
|
||||
try {
|
||||
@@ -25,16 +22,9 @@ export const transformEmailsValue = (
|
||||
: additionalEmails
|
||||
) as string[];
|
||||
|
||||
additionalEmails = JSON.stringify(
|
||||
emailArray.map((email) => email.toLowerCase()),
|
||||
);
|
||||
|
||||
if (isNullEquivalenceEnabled) {
|
||||
additionalEmails =
|
||||
Array.isArray(emailArray) && emailArray.length === 0
|
||||
? null
|
||||
: additionalEmails;
|
||||
}
|
||||
additionalEmails = isNonEmptyArray(emailArray)
|
||||
? JSON.stringify(emailArray.map((email) => email.toLowerCase()))
|
||||
: null;
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import {
|
||||
FieldMetadataType,
|
||||
compositeTypeDefinitions,
|
||||
} from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
FieldMetadataException,
|
||||
FieldMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import {
|
||||
IndexMetadataException,
|
||||
IndexMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/index-metadata/index-field-metadata.exception';
|
||||
|
||||
//TODO : To delete once IS_NULL_EQUIVALENCE_ENABLED feature flag removed
|
||||
export const computeUniqueIndexWhereClause = (
|
||||
fieldMetadata: Pick<FieldMetadataEntity, 'type' | 'name' | 'defaultValue'>,
|
||||
) => {
|
||||
const defaultValue = fieldMetadata.defaultValue;
|
||||
|
||||
if (!isDefined(defaultValue)) return;
|
||||
|
||||
if (
|
||||
fieldMetadata.type === FieldMetadataType.RELATION ||
|
||||
fieldMetadata.type === FieldMetadataType.MORPH_RELATION
|
||||
) {
|
||||
throw new IndexMetadataException(
|
||||
`Unique index cannot be created for relation or morph relation field ${fieldMetadata.name}`,
|
||||
IndexMetadataExceptionCode.INDEX_NOT_SUPPORTED_FOR_MORH_RELATION_FIELD_AND_RELATION_FIELD,
|
||||
);
|
||||
}
|
||||
|
||||
if (!isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
return `"${fieldMetadata.name}" != ${defaultValue}`;
|
||||
}
|
||||
|
||||
const compositeType = compositeTypeDefinitions.get(fieldMetadata.type);
|
||||
|
||||
if (!isDefined(compositeType)) {
|
||||
throw new FieldMetadataException(
|
||||
`Composite type not found for field metadata type: ${fieldMetadata.type}`,
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const defaultDefaultValueProperties = Object.keys(defaultValue);
|
||||
|
||||
const columnNamesWithDefaultValues = compositeType.properties
|
||||
.filter(
|
||||
(property) =>
|
||||
property.isIncludedInUniqueConstraint &&
|
||||
defaultDefaultValueProperties.includes(property.name),
|
||||
)
|
||||
.map((property) => {
|
||||
const defaultValueProperty =
|
||||
defaultValue[property.name as keyof typeof defaultValue];
|
||||
|
||||
if (isNonEmptyString(defaultValueProperty)) {
|
||||
return [
|
||||
computeCompositeColumnName(fieldMetadata, property),
|
||||
defaultValueProperty,
|
||||
];
|
||||
}
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
return columnNamesWithDefaultValues.length > 0
|
||||
? columnNamesWithDefaultValues
|
||||
.map(
|
||||
([columnName, defaultValue]) => `"${columnName}" != ${defaultValue}`,
|
||||
)
|
||||
.join(' OR ')
|
||||
: undefined;
|
||||
};
|
||||
-2
@@ -207,7 +207,6 @@ describe('WorkspaceEntityManager', () => {
|
||||
IS_WORKFLOW_RUN_STOPPAGE_ENABLED: false,
|
||||
IS_DASHBOARD_V2_ENABLED: false,
|
||||
IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED: false,
|
||||
IS_NULL_EQUIVALENCE_ENABLED: false,
|
||||
},
|
||||
eventEmitterService: {
|
||||
emitMutationEvent: jest.fn(),
|
||||
@@ -234,7 +233,6 @@ describe('WorkspaceEntityManager', () => {
|
||||
IS_WORKFLOW_RUN_STOPPAGE_ENABLED: false,
|
||||
IS_DASHBOARD_V2_ENABLED: false,
|
||||
IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED: false,
|
||||
IS_NULL_EQUIVALENCE_ENABLED: false,
|
||||
},
|
||||
permissionsPerRoleId: {},
|
||||
} as WorkspaceDataSource;
|
||||
|
||||
-5
@@ -92,11 +92,6 @@ export const seedFeatureFlags = async ({
|
||||
workspaceId: workspaceId,
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
key: FeatureFlagKey.IS_NULL_EQUIVALENCE_ENABLED,
|
||||
workspaceId: workspaceId,
|
||||
value: true,
|
||||
},
|
||||
])
|
||||
.execute();
|
||||
};
|
||||
|
||||
-1
@@ -2,5 +2,4 @@ import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/featu
|
||||
|
||||
export const DEFAULT_FEATURE_FLAGS = [
|
||||
FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED,
|
||||
FeatureFlagKey.IS_NULL_EQUIVALENCE_ENABLED,
|
||||
] as const satisfies FeatureFlagKey[];
|
||||
|
||||
Reference in New Issue
Block a user