Workspace migration v2 testing (#13136)
# Introduction Introduced `EachTesting` pattern for the builder unit tests. As always any suggestions are more than welcomed ! Still need to: - [x] implem basic tests for field - [x] create `get-flat-index-field-metadata.mock.ts` - [x] Implement basic tests for index and index-fields - [ ] Implem standard edges cases tests TDD style ## Misc - was https://github.com/twentyhq/twenty/pull/13132 closed due to mess to rebase on main
This commit is contained in:
@ -0,0 +1,54 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
||||
|
||||
type FlatFieldMetadataOverrides<
|
||||
T extends FieldMetadataType = FieldMetadataType,
|
||||
> = Required<
|
||||
Pick<FlatFieldMetadata<T>, 'uniqueIdentifier' | 'objectMetadataId'>
|
||||
> &
|
||||
Partial<FlatFieldMetadata<T>>;
|
||||
|
||||
export const getFlatFieldMetadataMock = <
|
||||
T extends FieldMetadataType = FieldMetadataType,
|
||||
>(
|
||||
overrides: FlatFieldMetadataOverrides<T>,
|
||||
): FlatFieldMetadata<T> => {
|
||||
const createdAt = faker.date.anytime();
|
||||
|
||||
return {
|
||||
createdAt,
|
||||
description: 'default flat field metadata description',
|
||||
icon: 'icon',
|
||||
id: faker.string.uuid(),
|
||||
isActive: true,
|
||||
isCustom: true,
|
||||
name: 'flatFieldMetadataName',
|
||||
label: 'flat field metadata label',
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
relationTargetFieldMetadataId: undefined,
|
||||
relationTargetObjectMetadataId: undefined,
|
||||
type: FieldMetadataType.TEXT as T,
|
||||
isLabelSyncedWithName: false,
|
||||
isSystem: false,
|
||||
standardId: undefined,
|
||||
standardOverrides: undefined,
|
||||
updatedAt: createdAt,
|
||||
workspaceId: faker.string.uuid(),
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
|
||||
export const getStandardFlatFieldMetadataMock = (
|
||||
overrides: Omit<FlatFieldMetadataOverrides, 'isCustom' | 'isSystem'>,
|
||||
) => {
|
||||
return getFlatFieldMetadataMock({
|
||||
standardId: faker.string.uuid(),
|
||||
standardOverrides: {},
|
||||
isCustom: false,
|
||||
isSystem: true,
|
||||
...overrides,
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,24 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
import { FlatIndexFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-field-metadata';
|
||||
|
||||
type FlatIndexFieldMetadataOverrides = Required<
|
||||
Pick<
|
||||
FlatIndexFieldMetadata,
|
||||
'fieldMetadataId' | 'indexMetadataId' | 'uniqueIdentifier'
|
||||
>
|
||||
> &
|
||||
Partial<FlatIndexFieldMetadata>;
|
||||
export const getFlatIndexFieldMetadataMock = (
|
||||
overrides: FlatIndexFieldMetadataOverrides,
|
||||
): FlatIndexFieldMetadata => {
|
||||
const createdAt = faker.date.anytime();
|
||||
|
||||
return {
|
||||
createdAt,
|
||||
id: faker.string.uuid(),
|
||||
order: faker.number.int(),
|
||||
updatedAt: createdAt,
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,37 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
|
||||
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
|
||||
|
||||
type FlatIndexMetadataOverrides = Required<
|
||||
Pick<FlatIndexMetadata, 'uniqueIdentifier' | 'objectMetadataId'>
|
||||
> &
|
||||
Partial<FlatIndexMetadata>;
|
||||
export const getFlatIndexMetadataMock = (
|
||||
overrides: FlatIndexMetadataOverrides,
|
||||
): FlatIndexMetadata => {
|
||||
const createdAt = faker.date.anytime();
|
||||
|
||||
return {
|
||||
flatIndexFieldMetadatas: [],
|
||||
createdAt,
|
||||
id: faker.string.uuid(),
|
||||
indexType: IndexType.BTREE,
|
||||
indexWhereClause: undefined,
|
||||
isCustom: false,
|
||||
isUnique: false,
|
||||
name: 'defaultFlatIndexMetadataName',
|
||||
updatedAt: createdAt,
|
||||
workspaceId: faker.string.uuid(),
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
|
||||
export const getStandardFlatIndexMetadataMock = (
|
||||
overrides: Omit<FlatIndexMetadataOverrides, 'isCustom'>,
|
||||
) => {
|
||||
return getFlatIndexMetadataMock({
|
||||
isCustom: false,
|
||||
...overrides,
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,56 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
|
||||
|
||||
type FlatObjectMetadataOverrides = Required<
|
||||
Pick<FlatObjectMetadata, 'uniqueIdentifier'>
|
||||
> &
|
||||
Partial<FlatObjectMetadata>;
|
||||
export const getFlatObjectMetadataMock = (
|
||||
overrides: FlatObjectMetadataOverrides,
|
||||
): FlatObjectMetadata => {
|
||||
const createdAt = faker.date.anytime();
|
||||
|
||||
return {
|
||||
flatFieldMetadatas: [],
|
||||
flatIndexMetadatas: [],
|
||||
createdAt,
|
||||
dataSourceId: faker.string.uuid(),
|
||||
description: 'default flat object metadata description',
|
||||
duplicateCriteria: [],
|
||||
icon: 'icon',
|
||||
id: faker.string.uuid(),
|
||||
imageIdentifierFieldMetadataId: faker.string.uuid(),
|
||||
isActive: true,
|
||||
isAuditLogged: true,
|
||||
isCustom: true,
|
||||
isLabelSyncedWithName: false,
|
||||
isRemote: false,
|
||||
isSearchable: true,
|
||||
isSystem: false,
|
||||
labelIdentifierFieldMetadataId: faker.string.uuid(),
|
||||
labelPlural: 'default flat object metadata label plural',
|
||||
labelSingular: 'default flat object metadata label singular',
|
||||
namePlural: 'defaultflatObjectMetadataNamePlural',
|
||||
nameSingular: 'defaultflatObjectMetadataNameSingular',
|
||||
shortcut: 'shortcut',
|
||||
standardId: undefined,
|
||||
standardOverrides: undefined,
|
||||
targetTableName: '',
|
||||
updatedAt: createdAt,
|
||||
workspaceId: faker.string.uuid(),
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
|
||||
export const getStandardFlatObjectMetadataMock = (
|
||||
overrides: Omit<FlatObjectMetadataOverrides, 'isCustom' | 'isSystem'>,
|
||||
) => {
|
||||
return getFlatObjectMetadataMock({
|
||||
standardId: faker.string.uuid(),
|
||||
standardOverrides: {},
|
||||
isCustom: false,
|
||||
isSystem: true,
|
||||
...overrides,
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,12 @@
|
||||
import { Relation } from 'typeorm';
|
||||
|
||||
export type ExtractRecordTypeOrmRelationProperties<T, TRelationTargets> =
|
||||
NonNullable<
|
||||
{
|
||||
[P in keyof T]: T[P] extends Relation<
|
||||
TRelationTargets | TRelationTargets[]
|
||||
>
|
||||
? P
|
||||
: never;
|
||||
}[keyof T]
|
||||
>;
|
||||
@ -1,7 +1,18 @@
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
export type FlatFieldMetadata = Partial<
|
||||
Omit<FieldMetadataEntity, 'object' | 'indexFieldMetadatas'>
|
||||
> & {
|
||||
uniqueIdentifier: string;
|
||||
};
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||
import { MetadataEntitiesRelationTarget } from 'src/engine/workspace-manager/workspace-migration-v2/types/metadata-entities-relation-targets.type';
|
||||
|
||||
type FieldMetadataEntityRelationProperties =
|
||||
ExtractRecordTypeOrmRelationProperties<
|
||||
FieldMetadataEntity,
|
||||
MetadataEntitiesRelationTarget
|
||||
>;
|
||||
|
||||
export type FlatFieldMetadata<T extends FieldMetadataType = FieldMetadataType> =
|
||||
Partial<
|
||||
Omit<FieldMetadataEntity<T>, FieldMetadataEntityRelationProperties>
|
||||
> & {
|
||||
uniqueIdentifier: string;
|
||||
};
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||
import { ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||
import { MetadataEntitiesRelationTarget } from 'src/engine/workspace-manager/workspace-migration-v2/types/metadata-entities-relation-targets.type';
|
||||
|
||||
const indexFieldMetadataEntityRelationProperties = [
|
||||
'indexMetadata',
|
||||
'fieldMetadata',
|
||||
] as const satisfies (keyof IndexFieldMetadataEntity)[];
|
||||
|
||||
type IndexFieldMetadataRelationProperties =
|
||||
(typeof indexFieldMetadataEntityRelationProperties)[number];
|
||||
type IndexFieldMetadataEntityRelationProperties =
|
||||
ExtractRecordTypeOrmRelationProperties<
|
||||
IndexFieldMetadataEntity,
|
||||
MetadataEntitiesRelationTarget
|
||||
>;
|
||||
|
||||
export type FlatIndexFieldMetadata = Partial<
|
||||
Omit<IndexFieldMetadataEntity, IndexFieldMetadataRelationProperties>
|
||||
Omit<IndexFieldMetadataEntity, IndexFieldMetadataEntityRelationProperties>
|
||||
> & {
|
||||
uniqueIdentifier: string;
|
||||
};
|
||||
|
||||
@ -1,8 +1,15 @@
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||
import { FlatIndexFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-field-metadata';
|
||||
import { MetadataEntitiesRelationTarget } from 'src/engine/workspace-manager/workspace-migration-v2/types/metadata-entities-relation-targets.type';
|
||||
|
||||
type IndexMetadataRelationProperties = ExtractRecordTypeOrmRelationProperties<
|
||||
IndexMetadataEntity,
|
||||
MetadataEntitiesRelationTarget
|
||||
>;
|
||||
|
||||
export type FlatIndexMetadata = Partial<
|
||||
Omit<IndexMetadataEntity, 'indexFieldMetadatas' | 'objectMetadata'> // Might have an issue as ObjectMetadataId != uniqueIdentifier
|
||||
Omit<IndexMetadataEntity, IndexMetadataRelationProperties>
|
||||
> & {
|
||||
flatIndexFieldMetadatas: FlatIndexFieldMetadata[];
|
||||
uniqueIdentifier: string;
|
||||
|
||||
@ -1,9 +1,16 @@
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
||||
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
|
||||
import { MetadataEntitiesRelationTarget } from 'src/engine/workspace-manager/workspace-migration-v2/types/metadata-entities-relation-targets.type';
|
||||
|
||||
type ObjectMetadataRelationProperties = ExtractRecordTypeOrmRelationProperties<
|
||||
ObjectMetadataEntity,
|
||||
MetadataEntitiesRelationTarget
|
||||
>;
|
||||
|
||||
export type FlatObjectMetadata = Partial<
|
||||
Omit<ObjectMetadataEntity, 'fields' | 'indexMetadatas'>
|
||||
Omit<ObjectMetadataEntity, ObjectMetadataRelationProperties>
|
||||
> & {
|
||||
uniqueIdentifier: string;
|
||||
flatIndexMetadatas: FlatIndexMetadata[];
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
|
||||
export type MetadataEntitiesRelationTarget =
|
||||
| ObjectMetadataEntity
|
||||
| FieldMetadataEntity
|
||||
| IndexFieldMetadataEntity
|
||||
| FieldPermissionEntity;
|
||||
@ -2,6 +2,7 @@ import diff from 'microdiff';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
||||
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
|
||||
import { UpdateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
@ -18,6 +19,11 @@ const flatFieldMetadataPropertiesToCompare = [
|
||||
'name',
|
||||
'options',
|
||||
'standardOverrides',
|
||||
'settings',
|
||||
// To reactivate once we authorize relation edition, see https://github.com/twentyhq/twenty/commit/39f6f3c4bb101272a9014e142a842d0801a3c33b
|
||||
// 'relationTargetFieldMetadataId',
|
||||
// 'relationTargetObjectMetadataId',
|
||||
///
|
||||
] as const satisfies (keyof FlatFieldMetadata)[];
|
||||
|
||||
export type FlatFieldMetadataPropertiesToCompare =
|
||||
@ -26,6 +32,7 @@ export type FlatFieldMetadataPropertiesToCompare =
|
||||
const fieldMetadataPropertiesToStringify = [
|
||||
'defaultValue',
|
||||
'standardOverrides',
|
||||
'settings',
|
||||
] as const satisfies FlatFieldMetadataPropertiesToCompare[];
|
||||
|
||||
const shouldNotOverrideDefaultValue = (type: FieldMetadataType) => {
|
||||
@ -65,6 +72,15 @@ export const compareTwoFlatFieldMetadata = ({
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove below assertion when we authorize relation edition, see https://github.com/twentyhq/twenty/commit/39f6f3c4bb101272a9014e142a842d0801a3c33b
|
||||
if (
|
||||
isDefined(fieldMetadata.type) &&
|
||||
isRelationFieldMetadataType(fieldMetadata.type) &&
|
||||
!['label', 'description', 'isActive'].includes(property)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
propertiesToStringify: fieldMetadataPropertiesToStringify,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,284 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { getFlatFieldMetadataMock } from 'src/engine/workspace-manager/workspace-migration-v2/__tests__/get-flat-field-metadata.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/workspace-manager/workspace-migration-v2/__tests__/get-flat-object-metadata.mock';
|
||||
import { WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||
|
||||
const basicObjectMetadataId = faker.string.uuid();
|
||||
const basicFlatFieldMetadatas = Array.from({ length: 5 }, (_value, index) =>
|
||||
getFlatFieldMetadataMock({
|
||||
objectMetadataId: basicObjectMetadataId,
|
||||
uniqueIdentifier: `field_${index}`,
|
||||
}),
|
||||
);
|
||||
|
||||
// TODO prastoin test defaultValue and settings updates
|
||||
// TODO prastoin test standard abstraction in TDD style
|
||||
|
||||
const relationTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||
{
|
||||
title: 'It should build an create_field action for a RELATION field',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
|
||||
const createdFlatRelationFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatFieldMetadatas: [],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: [createdFlatRelationFieldMetadata],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createField: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build an update_field action for a RELATION field',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
|
||||
const updatedFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatFieldMetadatas: [
|
||||
...basicFlatFieldMetadatas,
|
||||
updatedFieldMetadata,
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: [
|
||||
...basicFlatFieldMetadatas,
|
||||
{
|
||||
...updatedFieldMetadata,
|
||||
isActive: false,
|
||||
description: 'new description',
|
||||
label: 'new label',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
updateField: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'It should NOT build an update_field action for a field RELATION uncovered fields mutation',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
const updatedFieldMetadata =
|
||||
getFlatFieldMetadataMock<FieldMetadataType.RELATION>({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
settings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
isForeignKey: true,
|
||||
joinColumnName: 'column-name',
|
||||
onDelete: undefined,
|
||||
},
|
||||
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatFieldMetadatas: [updatedFieldMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: [
|
||||
{
|
||||
...updatedFieldMetadata,
|
||||
settings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
isForeignKey: false,
|
||||
joinColumnName: 'new-column-name',
|
||||
onDelete: undefined,
|
||||
},
|
||||
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const basicCrudTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||
{
|
||||
title: 'It should build an create_field action',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
|
||||
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatFieldMetadatas: [],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: [flatFieldMetadata],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createField: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build an update_field action',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
|
||||
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
flatFieldMetadatas: [...basicFlatFieldMetadatas, flatFieldMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: [
|
||||
...basicFlatFieldMetadatas,
|
||||
{
|
||||
...flatFieldMetadata,
|
||||
description: 'new description',
|
||||
name: 'new name',
|
||||
isActive: false,
|
||||
icon: 'new icon',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
updateField: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build a delete_field action',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
|
||||
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
flatFieldMetadatas: [...basicFlatFieldMetadatas, flatFieldMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: basicFlatFieldMetadatas,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
deleteField: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] =
|
||||
[
|
||||
...relationTestCases,
|
||||
...basicCrudTestCases,
|
||||
{
|
||||
title:
|
||||
'It should not infer any actions as from and to fields are identical',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const from = [
|
||||
getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
flatFieldMetadatas: [flatFieldMetadata],
|
||||
}),
|
||||
];
|
||||
|
||||
return {
|
||||
from,
|
||||
to: from,
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,130 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
import { getFlatIndexMetadataMock } from 'src/engine/workspace-manager/workspace-migration-v2/__tests__/get-flat-index-metadata.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/workspace-manager/workspace-migration-v2/__tests__/get-flat-object-metadata.mock';
|
||||
import { WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||
|
||||
// Should test more things such as flatFieldIndex diffing
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
|
||||
export const WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] =
|
||||
[
|
||||
{
|
||||
title: 'It should build an create_index action',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatIndexMetadatas: [],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatIndexMetadatas: [flatIndexMetadata],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'It should build an delete_index and a create_index action ( the way we handle update )',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatIndexMetadatas: [flatIndexMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatIndexMetadatas: [
|
||||
{
|
||||
...flatIndexMetadata,
|
||||
name: 'new index name',
|
||||
isUnique: false,
|
||||
indexWhereClause: 'new index where clause',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createIndex: 1,
|
||||
deleteIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build a delete_index action',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatIndexMetadatas: [flatIndexMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatIndexMetadatas: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
deleteIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'It should not infer any actions as from and to indexes are identical',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatIndexMetadatas: [flatIndexMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [flatObjectMetadata],
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,130 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
import { getFlatFieldMetadataMock } from 'src/engine/workspace-manager/workspace-migration-v2/__tests__/get-flat-field-metadata.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/workspace-manager/workspace-migration-v2/__tests__/get-flat-object-metadata.mock';
|
||||
import { WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||
|
||||
export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] =
|
||||
[
|
||||
{
|
||||
title:
|
||||
'It should build an update_object action with all object updated fields',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'pomme',
|
||||
nameSingular: 'toto',
|
||||
namePlural: 'totos',
|
||||
isLabelSyncedWithName: true,
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
nameSingular: 'prastouin',
|
||||
namePlural: 'prastoins',
|
||||
isLabelSyncedWithName: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
updateObject: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build a create_object action',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'pomme',
|
||||
nameSingular: 'toto',
|
||||
namePlural: 'totos',
|
||||
isLabelSyncedWithName: true,
|
||||
});
|
||||
|
||||
return {
|
||||
from: [],
|
||||
to: [flatObjectMetadata],
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createObject: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'It should build a create_object and create_field actions for each of this fieldMetadata',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
const flatFieldMetadatas = Array.from(
|
||||
{ length: 5 },
|
||||
(_value, index) =>
|
||||
getFlatFieldMetadataMock({
|
||||
objectMetadataId,
|
||||
uniqueIdentifier: `field_${index}`,
|
||||
}),
|
||||
);
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'pomme',
|
||||
nameSingular: 'toto',
|
||||
namePlural: 'totos',
|
||||
isLabelSyncedWithName: true,
|
||||
id: objectMetadataId,
|
||||
flatFieldMetadatas,
|
||||
});
|
||||
|
||||
return {
|
||||
from: [],
|
||||
to: [flatObjectMetadata],
|
||||
};
|
||||
},
|
||||
|
||||
expectedActionsTypeCounter: {
|
||||
createObject: 1,
|
||||
createField: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build a delete_object action',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'pomme',
|
||||
nameSingular: 'toto',
|
||||
namePlural: 'totos',
|
||||
isLabelSyncedWithName: true,
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [],
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
deleteObject: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should not infer any actions as from and to are identical',
|
||||
context: {
|
||||
input: () => {
|
||||
const from = [
|
||||
getFlatObjectMetadataMock({ uniqueIdentifier: 'pomme' }),
|
||||
];
|
||||
|
||||
return {
|
||||
from,
|
||||
to: from,
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,25 @@
|
||||
import { EachTestingContext } from 'twenty-shared/testing';
|
||||
|
||||
import { WorkspaceMigrationActionTypeV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
|
||||
type WorkspaceBuilderArgs = Parameters<
|
||||
typeof WorkspaceMigrationBuilderV2Service.prototype.build
|
||||
>[0];
|
||||
|
||||
type ConvertActionTypeToCamelCase<T extends string> =
|
||||
T extends `${infer Before}_${infer After}`
|
||||
? `${Before}${Capitalize<After>}`
|
||||
: T;
|
||||
|
||||
export type CamelCasedWorkspaceMigrationActionsType =
|
||||
ConvertActionTypeToCamelCase<WorkspaceMigrationActionTypeV2>;
|
||||
|
||||
export type ExpectedActionCounters = Partial<
|
||||
Record<CamelCasedWorkspaceMigrationActionsType, number>
|
||||
>;
|
||||
|
||||
export type WorkspaceMigrationBuilderTestCase = EachTestingContext<{
|
||||
input: WorkspaceBuilderArgs | (() => WorkspaceBuilderArgs);
|
||||
expectedActionsTypeCounter?: ExpectedActionCounters;
|
||||
}>;
|
||||
@ -0,0 +1,102 @@
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import { eachTestingContextFilter } from 'twenty-shared/testing';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
import { WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-field-test-case';
|
||||
import { WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-index-test-case';
|
||||
import { WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-object-test-case';
|
||||
import {
|
||||
CamelCasedWorkspaceMigrationActionsType,
|
||||
ExpectedActionCounters,
|
||||
WorkspaceMigrationBuilderTestCase,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||
import { WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-v2';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
|
||||
const allWorkspaceBuilderTestCases: {
|
||||
label: string;
|
||||
testCases: WorkspaceMigrationBuilderTestCase[];
|
||||
}[] = [
|
||||
{
|
||||
label: 'object',
|
||||
testCases: WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES,
|
||||
},
|
||||
{
|
||||
label: 'field',
|
||||
testCases: WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES,
|
||||
},
|
||||
{
|
||||
label: 'index',
|
||||
testCases: WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES,
|
||||
},
|
||||
];
|
||||
|
||||
const expectedActionsTypeCounterChecker = ({
|
||||
expectedActionsTypeCounter,
|
||||
workspaceMigration,
|
||||
}: {
|
||||
workspaceMigration: WorkspaceMigrationV2;
|
||||
expectedActionsTypeCounter?: ExpectedActionCounters;
|
||||
}) => {
|
||||
const initialAcc: ExpectedActionCounters = {
|
||||
createField: 0,
|
||||
createIndex: 0,
|
||||
createObject: 0,
|
||||
deleteField: 0,
|
||||
deleteIndex: 0,
|
||||
deleteObject: 0,
|
||||
updateField: 0,
|
||||
updateObject: 0,
|
||||
};
|
||||
const actualActionsTypeCounter = workspaceMigration.actions.reduce(
|
||||
(acc, action) => {
|
||||
const { type } = action;
|
||||
const [operation, target] = type.split('_');
|
||||
const formattedActionKey =
|
||||
`${operation}${capitalize(target)}` as CamelCasedWorkspaceMigrationActionsType;
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[formattedActionKey]: (acc[formattedActionKey] ?? 0) + 1,
|
||||
};
|
||||
},
|
||||
initialAcc,
|
||||
);
|
||||
|
||||
expect(actualActionsTypeCounter).toEqual({
|
||||
...initialAcc,
|
||||
...expectedActionsTypeCounter,
|
||||
});
|
||||
};
|
||||
|
||||
describe.each(allWorkspaceBuilderTestCases)(
|
||||
'Workspace migration builder $label actions test suite',
|
||||
({ testCases }) => {
|
||||
let service: WorkspaceMigrationBuilderV2Service;
|
||||
|
||||
beforeEach(() => {
|
||||
service = new WorkspaceMigrationBuilderV2Service();
|
||||
});
|
||||
|
||||
it.each(eachTestingContextFilter(testCases))(
|
||||
'$title',
|
||||
({ context: { input, expectedActionsTypeCounter } }) => {
|
||||
const { from, to } = typeof input === 'function' ? input() : input;
|
||||
const workspaceMigration = service.build({
|
||||
from,
|
||||
to,
|
||||
});
|
||||
|
||||
expectedActionsTypeCounterChecker({
|
||||
expectedActionsTypeCounter,
|
||||
workspaceMigration,
|
||||
});
|
||||
const { actions } = workspaceMigration;
|
||||
|
||||
expect(actions).toMatchSnapshot(
|
||||
actions.map(extractRecordIdsAndDatesAsExpectAny),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -1,605 +0,0 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
|
||||
describe('WorkspaceMigrationBuilderV2Service', () => {
|
||||
let service: WorkspaceMigrationBuilderV2Service;
|
||||
|
||||
const baseObject: FlatObjectMetadata = {
|
||||
uniqueIdentifier: '20202020-e89b-12d3-a456-426614175000',
|
||||
nameSingular: 'Contact',
|
||||
namePlural: 'Contacts',
|
||||
labelSingular: 'Contact',
|
||||
flatIndexMetadatas: [],
|
||||
labelPlural: 'Contacts',
|
||||
description: 'A contact',
|
||||
flatFieldMetadatas: [
|
||||
{
|
||||
uniqueIdentifier: '20202020-e89b-12d3-a456-426614174000',
|
||||
name: 'firstName',
|
||||
label: 'First Name',
|
||||
type: FieldMetadataType.FULL_NAME,
|
||||
defaultValue: '',
|
||||
description: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
service = new WorkspaceMigrationBuilderV2Service();
|
||||
});
|
||||
|
||||
it('should return a migration when nameSingular changes', () => {
|
||||
const from: FlatObjectMetadata = baseObject;
|
||||
const to: FlatObjectMetadata = {
|
||||
...from,
|
||||
nameSingular: 'Person',
|
||||
};
|
||||
const result = service.build({ from: [from], to: [to] });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"description": "A contact",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "First Name",
|
||||
"name": "firstName",
|
||||
"type": "FULL_NAME",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Contacts",
|
||||
"labelSingular": "Contact",
|
||||
"namePlural": "Contacts",
|
||||
"nameSingular": "Person",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
||||
},
|
||||
"type": "update_object",
|
||||
"updates": [
|
||||
{
|
||||
"from": "Contact",
|
||||
"property": "nameSingular",
|
||||
"to": "Person",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return a migration when creating a new object', () => {
|
||||
const newObject: FlatObjectMetadata = {
|
||||
uniqueIdentifier: '20202020-e89b-12d3-a456-426614175001',
|
||||
nameSingular: 'Company',
|
||||
namePlural: 'Companies',
|
||||
flatIndexMetadatas: [],
|
||||
labelSingular: 'Company',
|
||||
labelPlural: 'Companies',
|
||||
description: 'A company',
|
||||
flatFieldMetadatas: [
|
||||
{
|
||||
uniqueIdentifier: '20202020-e89b-12d3-a456-426614174001',
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: FieldMetadataType.ADDRESS,
|
||||
defaultValue: '',
|
||||
description: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = service.build({ from: [], to: [newObject] });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"description": "A company",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Name",
|
||||
"name": "name",
|
||||
"type": "ADDRESS",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Companies",
|
||||
"labelSingular": "Company",
|
||||
"namePlural": "Companies",
|
||||
"nameSingular": "Company",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175001",
|
||||
},
|
||||
"type": "create_object",
|
||||
},
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Name",
|
||||
"name": "name",
|
||||
"type": "ADDRESS",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"description": "A company",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Name",
|
||||
"name": "name",
|
||||
"type": "ADDRESS",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Companies",
|
||||
"labelSingular": "Company",
|
||||
"namePlural": "Companies",
|
||||
"nameSingular": "Company",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175001",
|
||||
},
|
||||
"type": "create_field",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return a migration when deleting an object', () => {
|
||||
const result = service.build({ from: [baseObject], to: [] });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"description": "A contact",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "First Name",
|
||||
"name": "firstName",
|
||||
"type": "FULL_NAME",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Contacts",
|
||||
"labelSingular": "Contact",
|
||||
"namePlural": "Contacts",
|
||||
"nameSingular": "Contact",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
||||
},
|
||||
"type": "delete_object",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle multiple operations in a single migration', () => {
|
||||
const objectToUpdate: FlatObjectMetadata = {
|
||||
...baseObject,
|
||||
nameSingular: 'Person',
|
||||
flatFieldMetadatas: [
|
||||
...baseObject.flatFieldMetadatas,
|
||||
{
|
||||
defaultValue: '',
|
||||
label: 'New field',
|
||||
type: FieldMetadataType.NUMBER,
|
||||
name: 'newField',
|
||||
uniqueIdentifier: '20202020-3ad3-4fec-9c46-8dc9158980e3',
|
||||
description: 'new field description',
|
||||
},
|
||||
],
|
||||
};
|
||||
const objectToDelete = {
|
||||
...baseObject,
|
||||
uniqueIdentifier: '20202020-59ef-4a14-a509-0a02acb248d5',
|
||||
};
|
||||
const objectToCreate: FlatObjectMetadata = {
|
||||
uniqueIdentifier: '20202020-1218-4fc0-b32d-fc4f005c4bab',
|
||||
nameSingular: 'Company',
|
||||
namePlural: 'Companies',
|
||||
flatIndexMetadatas: [],
|
||||
labelSingular: 'Company',
|
||||
labelPlural: 'Companies',
|
||||
description: 'A company',
|
||||
flatFieldMetadatas: [
|
||||
{
|
||||
uniqueIdentifier: '20202020-1016-4f09-bad6-e75681f385f4',
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: FieldMetadataType.ADDRESS,
|
||||
defaultValue: '',
|
||||
description: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = service.build({
|
||||
from: [baseObject, objectToDelete],
|
||||
to: [objectToUpdate, objectToCreate],
|
||||
});
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"description": "A company",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Name",
|
||||
"name": "name",
|
||||
"type": "ADDRESS",
|
||||
"uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Companies",
|
||||
"labelSingular": "Company",
|
||||
"namePlural": "Companies",
|
||||
"nameSingular": "Company",
|
||||
"uniqueIdentifier": "20202020-1218-4fc0-b32d-fc4f005c4bab",
|
||||
},
|
||||
"type": "create_object",
|
||||
},
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"description": "A contact",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "First Name",
|
||||
"name": "firstName",
|
||||
"type": "FULL_NAME",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Contacts",
|
||||
"labelSingular": "Contact",
|
||||
"namePlural": "Contacts",
|
||||
"nameSingular": "Contact",
|
||||
"uniqueIdentifier": "20202020-59ef-4a14-a509-0a02acb248d5",
|
||||
},
|
||||
"type": "delete_object",
|
||||
},
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"description": "A contact",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "First Name",
|
||||
"name": "firstName",
|
||||
"type": "FULL_NAME",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
||||
},
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "new field description",
|
||||
"label": "New field",
|
||||
"name": "newField",
|
||||
"type": "NUMBER",
|
||||
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Contacts",
|
||||
"labelSingular": "Contact",
|
||||
"namePlural": "Contacts",
|
||||
"nameSingular": "Person",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
||||
},
|
||||
"type": "update_object",
|
||||
"updates": [
|
||||
{
|
||||
"from": "Contact",
|
||||
"property": "nameSingular",
|
||||
"to": "Person",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Name",
|
||||
"name": "name",
|
||||
"type": "ADDRESS",
|
||||
"uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"description": "A company",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Name",
|
||||
"name": "name",
|
||||
"type": "ADDRESS",
|
||||
"uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Companies",
|
||||
"labelSingular": "Company",
|
||||
"namePlural": "Companies",
|
||||
"nameSingular": "Company",
|
||||
"uniqueIdentifier": "20202020-1218-4fc0-b32d-fc4f005c4bab",
|
||||
},
|
||||
"type": "create_field",
|
||||
},
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"defaultValue": "",
|
||||
"description": "new field description",
|
||||
"label": "New field",
|
||||
"name": "newField",
|
||||
"type": "NUMBER",
|
||||
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"description": "A contact",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "First Name",
|
||||
"name": "firstName",
|
||||
"type": "FULL_NAME",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
||||
},
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "new field description",
|
||||
"label": "New field",
|
||||
"name": "newField",
|
||||
"type": "NUMBER",
|
||||
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Contacts",
|
||||
"labelSingular": "Contact",
|
||||
"namePlural": "Contacts",
|
||||
"nameSingular": "Person",
|
||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
||||
},
|
||||
"type": "create_field",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should treat objects with the same name but different IDs as distinct', () => {
|
||||
const objectA: FlatObjectMetadata = {
|
||||
uniqueIdentifier: 'id-1',
|
||||
flatIndexMetadatas: [],
|
||||
nameSingular: 'Duplicate',
|
||||
namePlural: 'Duplicates',
|
||||
labelSingular: 'Duplicate',
|
||||
labelPlural: 'Duplicates',
|
||||
description: 'First object',
|
||||
flatFieldMetadatas: [
|
||||
{
|
||||
uniqueIdentifier: 'field-1',
|
||||
name: 'fieldA',
|
||||
label: 'Field A',
|
||||
type: FieldMetadataType.FULL_NAME,
|
||||
defaultValue: '',
|
||||
description: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
const objectB: FlatObjectMetadata = {
|
||||
uniqueIdentifier: 'id-2',
|
||||
nameSingular: 'Duplicate',
|
||||
namePlural: 'Duplicates',
|
||||
labelSingular: 'Duplicate',
|
||||
labelPlural: 'Duplicates',
|
||||
flatIndexMetadatas: [],
|
||||
description: 'Second object',
|
||||
flatFieldMetadatas: [
|
||||
{
|
||||
uniqueIdentifier: 'field-2',
|
||||
name: 'fieldB',
|
||||
label: 'Field B',
|
||||
type: FieldMetadataType.ADDRESS,
|
||||
defaultValue: '',
|
||||
description: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = service.build({ from: [], to: [objectA, objectB] });
|
||||
|
||||
expect(result.actions).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"description": "First object",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Field A",
|
||||
"name": "fieldA",
|
||||
"type": "FULL_NAME",
|
||||
"uniqueIdentifier": "field-1",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Duplicates",
|
||||
"labelSingular": "Duplicate",
|
||||
"namePlural": "Duplicates",
|
||||
"nameSingular": "Duplicate",
|
||||
"uniqueIdentifier": "id-1",
|
||||
},
|
||||
"type": "create_object",
|
||||
},
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"description": "Second object",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Field B",
|
||||
"name": "fieldB",
|
||||
"type": "ADDRESS",
|
||||
"uniqueIdentifier": "field-2",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Duplicates",
|
||||
"labelSingular": "Duplicate",
|
||||
"namePlural": "Duplicates",
|
||||
"nameSingular": "Duplicate",
|
||||
"uniqueIdentifier": "id-2",
|
||||
},
|
||||
"type": "create_object",
|
||||
},
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Field A",
|
||||
"name": "fieldA",
|
||||
"type": "FULL_NAME",
|
||||
"uniqueIdentifier": "field-1",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"description": "First object",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Field A",
|
||||
"name": "fieldA",
|
||||
"type": "FULL_NAME",
|
||||
"uniqueIdentifier": "field-1",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Duplicates",
|
||||
"labelSingular": "Duplicate",
|
||||
"namePlural": "Duplicates",
|
||||
"nameSingular": "Duplicate",
|
||||
"uniqueIdentifier": "id-1",
|
||||
},
|
||||
"type": "create_field",
|
||||
},
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Field B",
|
||||
"name": "fieldB",
|
||||
"type": "ADDRESS",
|
||||
"uniqueIdentifier": "field-2",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"description": "Second object",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Field B",
|
||||
"name": "fieldB",
|
||||
"type": "ADDRESS",
|
||||
"uniqueIdentifier": "field-2",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Duplicates",
|
||||
"labelSingular": "Duplicate",
|
||||
"namePlural": "Duplicates",
|
||||
"nameSingular": "Duplicate",
|
||||
"uniqueIdentifier": "id-2",
|
||||
},
|
||||
"type": "create_field",
|
||||
},
|
||||
]
|
||||
`);
|
||||
|
||||
const deleteResult = service.build({ from: [objectA, objectB], to: [] });
|
||||
|
||||
expect(deleteResult.actions).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"description": "First object",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Field A",
|
||||
"name": "fieldA",
|
||||
"type": "FULL_NAME",
|
||||
"uniqueIdentifier": "field-1",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Duplicates",
|
||||
"labelSingular": "Duplicate",
|
||||
"namePlural": "Duplicates",
|
||||
"nameSingular": "Duplicate",
|
||||
"uniqueIdentifier": "id-1",
|
||||
},
|
||||
"type": "delete_object",
|
||||
},
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"description": "Second object",
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"defaultValue": "",
|
||||
"description": "",
|
||||
"label": "Field B",
|
||||
"name": "fieldB",
|
||||
"type": "ADDRESS",
|
||||
"uniqueIdentifier": "field-2",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"labelPlural": "Duplicates",
|
||||
"labelSingular": "Duplicate",
|
||||
"namePlural": "Duplicates",
|
||||
"nameSingular": "Duplicate",
|
||||
"uniqueIdentifier": "id-2",
|
||||
},
|
||||
"type": "delete_object",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should emit no actions when from and to are deeply equal', () => {
|
||||
const obj: FlatObjectMetadata = { ...baseObject };
|
||||
const result = service.build({ from: [obj], to: [obj] });
|
||||
|
||||
expect(result.actions).toEqual([]);
|
||||
});
|
||||
});
|
||||
@ -1,522 +0,0 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
|
||||
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
||||
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
|
||||
import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
|
||||
describe('Workspace migration builder indexes tests suite', () => {
|
||||
let service: WorkspaceMigrationBuilderV2Service;
|
||||
|
||||
beforeEach(() => {
|
||||
service = new WorkspaceMigrationBuilderV2Service();
|
||||
});
|
||||
|
||||
const createMockObject = (
|
||||
identifier: string,
|
||||
fields: Partial<FlatFieldMetadata>[] = [],
|
||||
indexes: FlatIndexMetadata[] = [],
|
||||
): FlatObjectMetadata => ({
|
||||
uniqueIdentifier: identifier,
|
||||
flatIndexMetadatas: indexes,
|
||||
flatFieldMetadatas: fields.map((field) => ({
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'defaultName',
|
||||
label: 'Default Label',
|
||||
isCustom: true,
|
||||
isActive: true,
|
||||
isNullable: true,
|
||||
uniqueIdentifier: 'default-id',
|
||||
...field,
|
||||
})),
|
||||
});
|
||||
|
||||
const createMockIndex = (
|
||||
name: string,
|
||||
fields: string[],
|
||||
isUnique = false,
|
||||
): FlatIndexMetadata => ({
|
||||
name,
|
||||
isUnique,
|
||||
indexType: IndexType.BTREE,
|
||||
indexWhereClause: null,
|
||||
uniqueIdentifier: name,
|
||||
flatIndexFieldMetadatas: fields.map((field, index) => ({
|
||||
uniqueIdentifier: `${name}-field-${index}`,
|
||||
order: index,
|
||||
})),
|
||||
});
|
||||
|
||||
describe('buildWorkspaceMigrationV2IndexActions', () => {
|
||||
it('should create index actions for created indexes', () => {
|
||||
const fromObjects: FlatObjectMetadata[] = [];
|
||||
const toObjects: FlatObjectMetadata[] = [
|
||||
createMockObject(
|
||||
'company',
|
||||
[
|
||||
{
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
uniqueIdentifier: 'name',
|
||||
},
|
||||
],
|
||||
[createMockIndex('idx_company_name', ['name'], true)],
|
||||
),
|
||||
];
|
||||
|
||||
const result = service.build({ from: fromObjects, to: toObjects });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Name",
|
||||
"name": "name",
|
||||
"type": "TEXT",
|
||||
"uniqueIdentifier": "name",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [
|
||||
{
|
||||
"flatIndexFieldMetadatas": [
|
||||
{
|
||||
"order": 0,
|
||||
"uniqueIdentifier": "idx_company_name-field-0",
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": null,
|
||||
"isUnique": true,
|
||||
"name": "idx_company_name",
|
||||
"uniqueIdentifier": "idx_company_name",
|
||||
},
|
||||
],
|
||||
"uniqueIdentifier": "company",
|
||||
},
|
||||
"type": "create_object",
|
||||
},
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Name",
|
||||
"name": "name",
|
||||
"type": "TEXT",
|
||||
"uniqueIdentifier": "name",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Name",
|
||||
"name": "name",
|
||||
"type": "TEXT",
|
||||
"uniqueIdentifier": "name",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [
|
||||
{
|
||||
"flatIndexFieldMetadatas": [
|
||||
{
|
||||
"order": 0,
|
||||
"uniqueIdentifier": "idx_company_name-field-0",
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": null,
|
||||
"isUnique": true,
|
||||
"name": "idx_company_name",
|
||||
"uniqueIdentifier": "idx_company_name",
|
||||
},
|
||||
],
|
||||
"uniqueIdentifier": "company",
|
||||
},
|
||||
"type": "create_field",
|
||||
},
|
||||
{
|
||||
"flatIndexMetadata": {
|
||||
"flatIndexFieldMetadatas": [
|
||||
{
|
||||
"order": 0,
|
||||
"uniqueIdentifier": "idx_company_name-field-0",
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": null,
|
||||
"isUnique": true,
|
||||
"name": "idx_company_name",
|
||||
"uniqueIdentifier": "idx_company_name",
|
||||
},
|
||||
"type": "create_index",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should create delete actions for deleted indexes', () => {
|
||||
const fromObjects: FlatObjectMetadata[] = [
|
||||
createMockObject(
|
||||
'company',
|
||||
[
|
||||
{
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
uniqueIdentifier: 'name',
|
||||
},
|
||||
],
|
||||
[createMockIndex('idx_company_name', ['name'], true)],
|
||||
),
|
||||
];
|
||||
const toObjects: FlatObjectMetadata[] = [
|
||||
createMockObject('company', [
|
||||
{
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
uniqueIdentifier: 'name',
|
||||
},
|
||||
]),
|
||||
];
|
||||
|
||||
const result = service.build({ from: fromObjects, to: toObjects });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"flatIndexMetadata": {
|
||||
"flatIndexFieldMetadatas": [
|
||||
{
|
||||
"order": 0,
|
||||
"uniqueIdentifier": "idx_company_name-field-0",
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": null,
|
||||
"isUnique": true,
|
||||
"name": "idx_company_name",
|
||||
"uniqueIdentifier": "idx_company_name",
|
||||
},
|
||||
"type": "delete_index",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle multiple index changes across different objects', () => {
|
||||
const fromObjects: FlatObjectMetadata[] = [
|
||||
createMockObject(
|
||||
'company',
|
||||
[
|
||||
{
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
uniqueIdentifier: 'name',
|
||||
},
|
||||
],
|
||||
[createMockIndex('idx_company_name_old', ['name'], true)],
|
||||
),
|
||||
];
|
||||
const toObjects: FlatObjectMetadata[] = [
|
||||
createMockObject(
|
||||
'company',
|
||||
[
|
||||
{
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
uniqueIdentifier: 'name',
|
||||
},
|
||||
],
|
||||
[createMockIndex('idx_company_name_new', ['name'], true)],
|
||||
),
|
||||
createMockObject(
|
||||
'person',
|
||||
[
|
||||
{
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
uniqueIdentifier: 'email',
|
||||
},
|
||||
],
|
||||
[createMockIndex('idx_person_email', ['email'], true)],
|
||||
),
|
||||
];
|
||||
|
||||
const result = service.build({ from: fromObjects, to: toObjects });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Email",
|
||||
"name": "email",
|
||||
"type": "TEXT",
|
||||
"uniqueIdentifier": "email",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [
|
||||
{
|
||||
"flatIndexFieldMetadatas": [
|
||||
{
|
||||
"order": 0,
|
||||
"uniqueIdentifier": "idx_person_email-field-0",
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": null,
|
||||
"isUnique": true,
|
||||
"name": "idx_person_email",
|
||||
"uniqueIdentifier": "idx_person_email",
|
||||
},
|
||||
],
|
||||
"uniqueIdentifier": "person",
|
||||
},
|
||||
"type": "create_object",
|
||||
},
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Email",
|
||||
"name": "email",
|
||||
"type": "TEXT",
|
||||
"uniqueIdentifier": "email",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Email",
|
||||
"name": "email",
|
||||
"type": "TEXT",
|
||||
"uniqueIdentifier": "email",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [
|
||||
{
|
||||
"flatIndexFieldMetadatas": [
|
||||
{
|
||||
"order": 0,
|
||||
"uniqueIdentifier": "idx_person_email-field-0",
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": null,
|
||||
"isUnique": true,
|
||||
"name": "idx_person_email",
|
||||
"uniqueIdentifier": "idx_person_email",
|
||||
},
|
||||
],
|
||||
"uniqueIdentifier": "person",
|
||||
},
|
||||
"type": "create_field",
|
||||
},
|
||||
{
|
||||
"flatIndexMetadata": {
|
||||
"flatIndexFieldMetadatas": [
|
||||
{
|
||||
"order": 0,
|
||||
"uniqueIdentifier": "idx_person_email-field-0",
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": null,
|
||||
"isUnique": true,
|
||||
"name": "idx_person_email",
|
||||
"uniqueIdentifier": "idx_person_email",
|
||||
},
|
||||
"type": "create_index",
|
||||
},
|
||||
{
|
||||
"flatIndexMetadata": {
|
||||
"flatIndexFieldMetadatas": [
|
||||
{
|
||||
"order": 0,
|
||||
"uniqueIdentifier": "idx_company_name_new-field-0",
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": null,
|
||||
"isUnique": true,
|
||||
"name": "idx_company_name_new",
|
||||
"uniqueIdentifier": "idx_company_name_new",
|
||||
},
|
||||
"type": "create_index",
|
||||
},
|
||||
{
|
||||
"flatIndexMetadata": {
|
||||
"flatIndexFieldMetadatas": [
|
||||
{
|
||||
"order": 0,
|
||||
"uniqueIdentifier": "idx_company_name_old-field-0",
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": null,
|
||||
"isUnique": true,
|
||||
"name": "idx_company_name_old",
|
||||
"uniqueIdentifier": "idx_company_name_old",
|
||||
},
|
||||
"type": "delete_index",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle empty objects', () => {
|
||||
const result = service.build({ from: [], to: [] });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle objects with no index changes', () => {
|
||||
const objects = [
|
||||
createMockObject('company', [
|
||||
{
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
uniqueIdentifier: 'name',
|
||||
},
|
||||
]),
|
||||
];
|
||||
|
||||
const result = service.build({ from: objects, to: objects });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle index updates', () => {
|
||||
const fromObjects: FlatObjectMetadata[] = [
|
||||
createMockObject(
|
||||
'company',
|
||||
[
|
||||
{
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
uniqueIdentifier: 'name',
|
||||
},
|
||||
],
|
||||
[createMockIndex('idx_company_name', ['name'], true)],
|
||||
),
|
||||
];
|
||||
|
||||
const toObjects: FlatObjectMetadata[] = [
|
||||
createMockObject(
|
||||
'company',
|
||||
[
|
||||
{
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
uniqueIdentifier: 'name',
|
||||
},
|
||||
],
|
||||
[createMockIndex('idx_company_name', ['name'], false)],
|
||||
),
|
||||
];
|
||||
|
||||
const result = service.build({ from: fromObjects, to: toObjects });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"flatIndexMetadata": {
|
||||
"flatIndexFieldMetadatas": [
|
||||
{
|
||||
"order": 0,
|
||||
"uniqueIdentifier": "idx_company_name-field-0",
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": null,
|
||||
"isUnique": true,
|
||||
"name": "idx_company_name",
|
||||
"uniqueIdentifier": "idx_company_name",
|
||||
},
|
||||
"type": "delete_index",
|
||||
},
|
||||
{
|
||||
"flatIndexMetadata": {
|
||||
"flatIndexFieldMetadatas": [
|
||||
{
|
||||
"order": 0,
|
||||
"uniqueIdentifier": "idx_company_name-field-0",
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"indexWhereClause": null,
|
||||
"isUnique": false,
|
||||
"name": "idx_company_name",
|
||||
"uniqueIdentifier": "idx_company_name",
|
||||
},
|
||||
"type": "create_index",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not generate any actions when indexes are identical', () => {
|
||||
const objects = [
|
||||
createMockObject(
|
||||
'company',
|
||||
[
|
||||
{
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
uniqueIdentifier: 'name',
|
||||
},
|
||||
],
|
||||
[createMockIndex('idx_company_name', ['name'], true)],
|
||||
),
|
||||
];
|
||||
|
||||
const result = service.build({ from: objects, to: objects });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,431 +0,0 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-on-delete-action.interface';
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
||||
import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
|
||||
describe('Workspace migration builder relations tests suite', () => {
|
||||
let service: WorkspaceMigrationBuilderV2Service;
|
||||
|
||||
beforeEach(() => {
|
||||
service = new WorkspaceMigrationBuilderV2Service();
|
||||
});
|
||||
|
||||
const createMockObject = (
|
||||
identifier: string,
|
||||
fields: Partial<FlatFieldMetadata>[] = [],
|
||||
): FlatObjectMetadata => ({
|
||||
uniqueIdentifier: identifier,
|
||||
flatIndexMetadatas: [],
|
||||
flatFieldMetadatas: fields.map((field) => ({
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'defaultName',
|
||||
label: 'Default Label',
|
||||
isCustom: true,
|
||||
isActive: true,
|
||||
isNullable: true,
|
||||
uniqueIdentifier: 'default-id',
|
||||
...field,
|
||||
})),
|
||||
});
|
||||
|
||||
describe('buildWorkspaceMigrationV2RelationActions', () => {
|
||||
it('should create relation actions for created fields', () => {
|
||||
const fromObjects: FlatObjectMetadata[] = [];
|
||||
const toObjects: FlatObjectMetadata[] = [
|
||||
createMockObject('company', [
|
||||
{
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'employees',
|
||||
label: 'Employees',
|
||||
uniqueIdentifier: 'employees',
|
||||
relationTargetFieldMetadataId: 'field-2',
|
||||
relationTargetObjectMetadataId: 'obj-2',
|
||||
},
|
||||
]),
|
||||
];
|
||||
|
||||
const result = service.build({ from: fromObjects, to: toObjects });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Employees",
|
||||
"name": "employees",
|
||||
"relationTargetFieldMetadataId": "field-2",
|
||||
"relationTargetObjectMetadataId": "obj-2",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "employees",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"uniqueIdentifier": "company",
|
||||
},
|
||||
"type": "create_object",
|
||||
},
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Employees",
|
||||
"name": "employees",
|
||||
"relationTargetFieldMetadataId": "field-2",
|
||||
"relationTargetObjectMetadataId": "obj-2",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "employees",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Employees",
|
||||
"name": "employees",
|
||||
"relationTargetFieldMetadataId": "field-2",
|
||||
"relationTargetObjectMetadataId": "obj-2",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "employees",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"uniqueIdentifier": "company",
|
||||
},
|
||||
"type": "create_field",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should create delete actions for deleted fields', () => {
|
||||
const fromObjects: FlatObjectMetadata[] = [
|
||||
createMockObject('company', [
|
||||
{
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'employees',
|
||||
label: 'Employees',
|
||||
uniqueIdentifier: 'employees',
|
||||
relationTargetFieldMetadataId: 'field-2',
|
||||
relationTargetObjectMetadataId: 'obj-2',
|
||||
},
|
||||
]),
|
||||
];
|
||||
const toObjects: FlatObjectMetadata[] = [createMockObject('company')];
|
||||
|
||||
const result = service.build({ from: fromObjects, to: toObjects });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Employees",
|
||||
"name": "employees",
|
||||
"relationTargetFieldMetadataId": "field-2",
|
||||
"relationTargetObjectMetadataId": "obj-2",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "employees",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"flatFieldMetadatas": [],
|
||||
"flatIndexMetadatas": [],
|
||||
"uniqueIdentifier": "company",
|
||||
},
|
||||
"type": "delete_field",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle multiple relation changes across different objects', () => {
|
||||
const fromObjects: FlatObjectMetadata[] = [
|
||||
createMockObject('company', [
|
||||
{
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'oldRelation',
|
||||
label: 'Old Relation',
|
||||
uniqueIdentifier: 'old-relation',
|
||||
relationTargetFieldMetadataId: 'field-1',
|
||||
relationTargetObjectMetadataId: 'obj-1',
|
||||
},
|
||||
]),
|
||||
];
|
||||
const toObjects: FlatObjectMetadata[] = [
|
||||
createMockObject('company', [
|
||||
{
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'newRelation',
|
||||
label: 'New Relation',
|
||||
uniqueIdentifier: 'new-relation',
|
||||
relationTargetFieldMetadataId: 'field-2',
|
||||
relationTargetObjectMetadataId: 'obj-2',
|
||||
},
|
||||
]),
|
||||
createMockObject('person', [
|
||||
{
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'manager',
|
||||
label: 'Manager',
|
||||
uniqueIdentifier: 'manager',
|
||||
relationTargetFieldMetadataId: 'field-3',
|
||||
relationTargetObjectMetadataId: 'obj-3',
|
||||
},
|
||||
]),
|
||||
];
|
||||
|
||||
const result = service.build({ from: fromObjects, to: toObjects });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"flatObjectMetadata": {
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Manager",
|
||||
"name": "manager",
|
||||
"relationTargetFieldMetadataId": "field-3",
|
||||
"relationTargetObjectMetadataId": "obj-3",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "manager",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"uniqueIdentifier": "person",
|
||||
},
|
||||
"type": "create_object",
|
||||
},
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Manager",
|
||||
"name": "manager",
|
||||
"relationTargetFieldMetadataId": "field-3",
|
||||
"relationTargetObjectMetadataId": "obj-3",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "manager",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Manager",
|
||||
"name": "manager",
|
||||
"relationTargetFieldMetadataId": "field-3",
|
||||
"relationTargetObjectMetadataId": "obj-3",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "manager",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"uniqueIdentifier": "person",
|
||||
},
|
||||
"type": "create_field",
|
||||
},
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "New Relation",
|
||||
"name": "newRelation",
|
||||
"relationTargetFieldMetadataId": "field-2",
|
||||
"relationTargetObjectMetadataId": "obj-2",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "new-relation",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "New Relation",
|
||||
"name": "newRelation",
|
||||
"relationTargetFieldMetadataId": "field-2",
|
||||
"relationTargetObjectMetadataId": "obj-2",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "new-relation",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"uniqueIdentifier": "company",
|
||||
},
|
||||
"type": "create_field",
|
||||
},
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Old Relation",
|
||||
"name": "oldRelation",
|
||||
"relationTargetFieldMetadataId": "field-1",
|
||||
"relationTargetObjectMetadataId": "obj-1",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "old-relation",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "New Relation",
|
||||
"name": "newRelation",
|
||||
"relationTargetFieldMetadataId": "field-2",
|
||||
"relationTargetObjectMetadataId": "obj-2",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "new-relation",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"uniqueIdentifier": "company",
|
||||
},
|
||||
"type": "delete_field",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle empty objects', () => {
|
||||
const result = service.build({ from: [], to: [] });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle objects with no relation changes', () => {
|
||||
const objects = [
|
||||
createMockObject('company', [
|
||||
{
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
uniqueIdentifier: 'name',
|
||||
},
|
||||
]),
|
||||
];
|
||||
|
||||
const result = service.build({ from: objects, to: objects });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle relation field updates', () => {
|
||||
const baseField = {
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'employees',
|
||||
label: 'Employees',
|
||||
uniqueIdentifier: 'employees',
|
||||
isCustom: true,
|
||||
isActive: true,
|
||||
isNullable: true,
|
||||
description: 'Company employees',
|
||||
};
|
||||
|
||||
const fromObjects: FlatObjectMetadata[] = [
|
||||
createMockObject('company', [
|
||||
{
|
||||
...baseField,
|
||||
relationTargetFieldMetadataId: 'field-1',
|
||||
relationTargetObjectMetadataId: 'obj-1',
|
||||
settings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
} as FieldMetadataSettings<FieldMetadataType.RELATION>,
|
||||
},
|
||||
]),
|
||||
];
|
||||
|
||||
const toObjects: FlatObjectMetadata[] = [
|
||||
{
|
||||
...fromObjects[0],
|
||||
flatFieldMetadatas: [
|
||||
{
|
||||
...baseField,
|
||||
name: 'updatedName',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const result = service.build({ from: fromObjects, to: toObjects });
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"flatFieldMetadata": {
|
||||
"description": "Company employees",
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Employees",
|
||||
"name": "updatedName",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "employees",
|
||||
},
|
||||
"flatObjectMetadata": {
|
||||
"flatFieldMetadatas": [
|
||||
{
|
||||
"description": "Company employees",
|
||||
"isActive": true,
|
||||
"isCustom": true,
|
||||
"isNullable": true,
|
||||
"label": "Employees",
|
||||
"name": "updatedName",
|
||||
"type": "RELATION",
|
||||
"uniqueIdentifier": "employees",
|
||||
},
|
||||
],
|
||||
"flatIndexMetadatas": [],
|
||||
"uniqueIdentifier": "company",
|
||||
},
|
||||
"type": "update_field",
|
||||
"updates": [
|
||||
{
|
||||
"from": "employees",
|
||||
"property": "name",
|
||||
"to": "updatedName",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,4 +1,4 @@
|
||||
import { faker } from '@faker-js/faker/.';
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const extractRecordIdsAndDatesAsExpectAny = (
|
||||
record: Record<string, unknown> | Array<Record<string, unknown>>,
|
||||
): any => {
|
||||
if (Array.isArray(record)) {
|
||||
return record.map(extractRecordIdsAndDatesAsExpectAny);
|
||||
}
|
||||
|
||||
if (typeof record !== 'object') {
|
||||
throw new Error(
|
||||
'extractRecordIdsAndDatesAsExpectAny should be called with an array or a record only',
|
||||
);
|
||||
}
|
||||
|
||||
return Object.entries(record).reduce((acc, [key, value]) => {
|
||||
if (!isDefined(value)) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (key.endsWith('Id') || key === 'id') {
|
||||
return {
|
||||
...acc,
|
||||
[key]: expect.any(String),
|
||||
};
|
||||
}
|
||||
|
||||
if (value instanceof Date) {
|
||||
return {
|
||||
...acc,
|
||||
[key]: expect.any(Date),
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof value === 'object' || Array.isArray(value)) {
|
||||
return {
|
||||
...acc,
|
||||
[key]: extractRecordIdsAndDatesAsExpectAny(
|
||||
value as Record<string, unknown>,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
@ -0,0 +1,20 @@
|
||||
import { EachTestingContext } from '@/testing/types/EachTestingContext.type';
|
||||
|
||||
export const eachTestingContextFilter = <T>(
|
||||
testCases: EachTestingContext<T>[],
|
||||
) => {
|
||||
const onlyTestsCases = testCases.filter((testCase) => testCase.only === true);
|
||||
|
||||
if (process.env.CI && onlyTestsCases.length > 0) {
|
||||
console.warn(
|
||||
'Should never push tests cases with an only to true, only to use in dev env\n returning the whole test suite anyway',
|
||||
);
|
||||
return testCases;
|
||||
}
|
||||
|
||||
if (onlyTestsCases.length > 0) {
|
||||
return onlyTestsCases;
|
||||
}
|
||||
|
||||
return testCases;
|
||||
};
|
||||
@ -7,5 +7,6 @@
|
||||
* |___/
|
||||
*/
|
||||
|
||||
export { eachTestingContextFilter } from './EachTestingContextFilter';
|
||||
export type { EachTestingContext } from './types/EachTestingContext.type';
|
||||
export type { SuccessfulAndFailingTestCases } from './types/SuccessfulAndFailingTestCases';
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
export type EachTestingContext<T> = {
|
||||
title: string;
|
||||
context: T;
|
||||
only?: true;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user