Fix storybook / chromatic tests flakyness and integration tests (#11687)
## Storybook flakyness ### Actor Display image flakyness <img width="1512" alt="image" src="https://github.com/user-attachments/assets/875c0738-5e31-4aba-9231-4ba5f78d1355" /> **Fix:** stop using a random usage ### Task Groups broken <img width="1512" alt="image" src="https://github.com/user-attachments/assets/c67e47a1-a027-43f1-9601-68d61a8052b4" /> **Fix:** add missing TabListComponentInstance ## Flaky dates Add https://github.com/k35o/storybook-addon-mock-date ## Integration tests Fix broken tests due to relation refactoring
This commit is contained in:
@ -183,6 +183,7 @@
|
||||
"semver": "^7.5.4",
|
||||
"sharp": "^0.32.1",
|
||||
"slash": "^5.1.0",
|
||||
"storybook-addon-mock-date": "^0.6.0",
|
||||
"stripe": "^17.3.1",
|
||||
"ts-key-enum": "^2.0.12",
|
||||
"tslib": "^2.3.0",
|
||||
|
||||
@ -48,6 +48,7 @@ const config: StorybookConfig = {
|
||||
'storybook-dark-mode',
|
||||
'storybook-addon-cookie',
|
||||
'storybook-addon-pseudo-states',
|
||||
'storybook-addon-mock-date'
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/react-vite',
|
||||
|
||||
@ -62,6 +62,7 @@ const preview: Preview = {
|
||||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
mockingDate: new Date('2024-03-12T09:30:00.000Z'),
|
||||
options: {
|
||||
storySort: {
|
||||
order: ['UI', 'Modules', 'Pages'],
|
||||
|
||||
@ -12,11 +12,11 @@ const StyledContainer = styled.div`
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
export const ObjectTasks = ({
|
||||
targetableObject,
|
||||
}: {
|
||||
type ObjectTasksProps = {
|
||||
targetableObject: ActivityTargetableObject;
|
||||
}) => {
|
||||
};
|
||||
|
||||
export const ObjectTasks = ({ targetableObject }: ObjectTasksProps) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<ObjectFilterDropdownComponentInstanceContext.Provider
|
||||
|
||||
@ -3,6 +3,7 @@ import { Meta, StoryObj } from '@storybook/react';
|
||||
import { TaskGroups } from '@/activities/tasks/components/TaskGroups';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
|
||||
import { TabListComponentInstanceContext } from '@/ui/layout/tab/states/contexts/TabListComponentInstanceContext';
|
||||
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
@ -15,11 +16,15 @@ const meta: Meta<typeof TaskGroups> = {
|
||||
component: TaskGroups,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<TabListComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'entity-tasks-filter-scope' }}
|
||||
>
|
||||
<ObjectFilterDropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'entity-tasks-filter-scope' }}
|
||||
>
|
||||
<Story />
|
||||
</ObjectFilterDropdownComponentInstanceContext.Provider>
|
||||
</TabListComponentInstanceContext.Provider>
|
||||
),
|
||||
ComponentWithRouterDecorator,
|
||||
ComponentWithRecoilScopeDecorator,
|
||||
|
||||
@ -1,38 +1,39 @@
|
||||
import { ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useFilterActionsWithCommandMenuSearch = ({
|
||||
commandMenuSearch,
|
||||
}: {
|
||||
commandMenuSearch: string;
|
||||
}) => {
|
||||
const [deferredCommandMenuSearch] = useDebounce(commandMenuSearch, 300); // 200ms - 500ms
|
||||
|
||||
const checkInShortcuts = (action: ActionConfig, search: string) => {
|
||||
const checkInShortcuts = (action: ActionConfig, search: string) => {
|
||||
const concatenatedString = action.hotKeys?.join('') ?? '';
|
||||
return concatenatedString
|
||||
.toLowerCase()
|
||||
.includes(search.toLowerCase().trim());
|
||||
};
|
||||
return concatenatedString.toLowerCase().includes(search.toLowerCase().trim());
|
||||
};
|
||||
|
||||
const checkInLabels = (action: ActionConfig, search: string) => {
|
||||
const checkInLabels = (action: ActionConfig, search: string) => {
|
||||
const actionLabel = getActionLabel(action.label);
|
||||
if (isNonEmptyString(actionLabel)) {
|
||||
return actionLabel.toLowerCase().includes(search.toLowerCase());
|
||||
}
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
const filterActionsWithCommandMenuSearch = (actions: ActionConfig[]) => {
|
||||
type UseFilterActionsWithCommandMenuSearchProps = {
|
||||
commandMenuSearch: string;
|
||||
};
|
||||
|
||||
export const useFilterActionsWithCommandMenuSearch = ({
|
||||
commandMenuSearch,
|
||||
}: UseFilterActionsWithCommandMenuSearchProps) => {
|
||||
const filterActionsWithCommandMenuSearch = useCallback(
|
||||
(actions: ActionConfig[]) => {
|
||||
return actions.filter((action) =>
|
||||
deferredCommandMenuSearch.length > 0
|
||||
? checkInShortcuts(action, deferredCommandMenuSearch) ||
|
||||
checkInLabels(action, deferredCommandMenuSearch)
|
||||
commandMenuSearch.length > 0
|
||||
? checkInShortcuts(action, commandMenuSearch) ||
|
||||
checkInLabels(action, commandMenuSearch)
|
||||
: true,
|
||||
);
|
||||
};
|
||||
},
|
||||
[commandMenuSearch],
|
||||
);
|
||||
|
||||
return {
|
||||
filterActionsWithCommandMenuSearch,
|
||||
|
||||
@ -4,6 +4,7 @@ import { Field, FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { FieldMetadataItem } from '../types/FieldMetadataItem';
|
||||
import { formatFieldMetadataItemInput } from '../utils/formatFieldMetadataItemInput';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useCreateOneFieldMetadataItem } from './useCreateOneFieldMetadataItem';
|
||||
import { useDeleteOneFieldMetadataItem } from './useDeleteOneFieldMetadataItem';
|
||||
import { useUpdateOneFieldMetadataItem } from './useUpdateOneFieldMetadataItem';
|
||||
@ -63,7 +64,8 @@ export const useFieldMetadataItem = () => {
|
||||
});
|
||||
|
||||
const deleteMetadataField = (metadataField: FieldMetadataItem) => {
|
||||
return metadataField.type === FieldMetadataType.RELATION
|
||||
return metadataField.type === FieldMetadataType.RELATION &&
|
||||
!isDefined(metadataField.settings?.relationType)
|
||||
? deleteOneRelationMetadataItem(
|
||||
metadataField.relationDefinition?.relationId,
|
||||
)
|
||||
|
||||
@ -15,16 +15,16 @@ import { tableColumnsComponentState } from '@/object-record/record-table/states/
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
|
||||
import { within } from '@storybook/test';
|
||||
import {
|
||||
ComponentDecorator,
|
||||
getCanvasElementForDropdownTesting,
|
||||
} from 'twenty-ui/testing';
|
||||
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { IconsProviderDecorator } from '~/testing/decorators/IconsProviderDecorator';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import {
|
||||
ComponentDecorator,
|
||||
getCanvasElementForDropdownTesting,
|
||||
} from 'twenty-ui/testing';
|
||||
|
||||
const meta: Meta<typeof MultipleFiltersDropdownButton> = {
|
||||
title:
|
||||
|
||||
@ -37,7 +37,7 @@ export const Catalog: Story = {
|
||||
},
|
||||
{
|
||||
name: 'avatarUrl',
|
||||
values: [null, 'https://picsum.photos/16'],
|
||||
values: [null, 'https://picsum.photos/id/237/16/16'],
|
||||
props: (avatarUrl: string) => ({ avatarUrl }),
|
||||
},
|
||||
],
|
||||
|
||||
@ -9,8 +9,6 @@ import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { userEvent, within } from '@storybook/test';
|
||||
import { SettingsExperience } from '../profile/appearance/components/SettingsExperience';
|
||||
|
||||
Date.now = () => new Date('2022-06-13T12:33:37.000Z').getTime();
|
||||
|
||||
const meta: Meta<PageDecoratorArgs> = {
|
||||
title: 'Pages/Settings/SettingsExperience',
|
||||
component: SettingsExperience,
|
||||
@ -44,15 +42,15 @@ export const DateTimeSettingsTimeFormat: Story = {
|
||||
|
||||
await canvas.findByText('Date and time');
|
||||
|
||||
const timeFormatSelect = await canvas.findByText('24h (08:33)');
|
||||
const timeFormatSelect = await canvas.findByText('24h (05:30)');
|
||||
|
||||
await userEvent.click(timeFormatSelect);
|
||||
|
||||
const timeFormatOptions = await canvas.findByText('12h (8:33 AM)');
|
||||
const timeFormatOptions = await canvas.findByText('12h (5:30 AM)');
|
||||
|
||||
await userEvent.click(timeFormatOptions);
|
||||
|
||||
await canvas.findByText('12h (8:33 AM)');
|
||||
await canvas.findByText('12h (5:30 AM)');
|
||||
},
|
||||
};
|
||||
|
||||
@ -84,14 +82,14 @@ export const DateTimeSettingsDateFormat: Story = {
|
||||
|
||||
await canvas.findByText('Date and time');
|
||||
|
||||
const timeFormatSelect = await canvas.findByText('13 Jun, 2022');
|
||||
const timeFormatSelect = await canvas.findByText('12 Mar, 2024');
|
||||
|
||||
await userEvent.click(timeFormatSelect);
|
||||
|
||||
const timeFormatOptions = await canvas.findByText('Jun 13, 2022');
|
||||
const timeFormatOptions = await canvas.findByText('Mar 12, 2024');
|
||||
|
||||
await userEvent.click(timeFormatOptions);
|
||||
|
||||
await canvas.findByText('Jun 13, 2022');
|
||||
await canvas.findByText('Mar 12, 2024');
|
||||
},
|
||||
};
|
||||
|
||||
@ -37,11 +37,6 @@ export class WorkspaceSchemaFactory {
|
||||
return new GraphQLSchema({});
|
||||
}
|
||||
|
||||
const cachedIsNewRelationEnabled =
|
||||
await this.workspaceCacheStorageService.getIsNewRelationEnabled(
|
||||
authContext.workspace.id,
|
||||
);
|
||||
|
||||
const isNewRelationEnabled = await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsNewRelationEnabled,
|
||||
authContext.workspace.id,
|
||||
@ -79,35 +74,6 @@ export class WorkspaceSchemaFactory {
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: remove this after the feature flag is droped
|
||||
if (
|
||||
(isNewRelationEnabled && cachedIsNewRelationEnabled === undefined) ||
|
||||
(isNewRelationEnabled !== cachedIsNewRelationEnabled &&
|
||||
cachedIsNewRelationEnabled !== undefined)
|
||||
) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
chalk.yellow('Recomputing due to new relation feature flag'),
|
||||
{
|
||||
isNewRelationEnabled,
|
||||
},
|
||||
);
|
||||
|
||||
await this.workspaceCacheStorageService.setIsNewRelationEnabled(
|
||||
authContext.workspace.id,
|
||||
isNewRelationEnabled,
|
||||
);
|
||||
|
||||
await this.workspaceMetadataCacheService.recomputeMetadataCache({
|
||||
workspaceId: authContext.workspace.id,
|
||||
});
|
||||
|
||||
throw new GraphqlQueryRunnerException(
|
||||
'Metadata cache recomputation required due to relation feature flag change',
|
||||
GraphqlQueryRunnerExceptionCode.METADATA_CACHE_FEATURE_FLAG_RECOMPUTATION_REQUIRED,
|
||||
);
|
||||
}
|
||||
|
||||
const objectMetadataMaps =
|
||||
await this.workspaceCacheStorageService.getObjectMetadataMaps(
|
||||
authContext.workspace.id,
|
||||
|
||||
@ -24,6 +24,7 @@ import { IsFieldMetadataOptions } from 'src/engine/metadata-modules/field-metada
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
@ -41,7 +42,7 @@ import { UpdateFieldInput } from './dtos/update-field.input';
|
||||
NestjsQueryGraphQLModule.forFeature({
|
||||
imports: [
|
||||
NestjsQueryTypeOrmModule.forFeature(
|
||||
[FieldMetadataEntity, ObjectMetadataEntity],
|
||||
[FieldMetadataEntity, ObjectMetadataEntity, RelationMetadataEntity],
|
||||
'metadata',
|
||||
),
|
||||
WorkspaceMigrationModule,
|
||||
|
||||
@ -161,12 +161,6 @@ export class FieldMetadataResolver {
|
||||
throw new ValidationError("Active fields can't be deleted");
|
||||
}
|
||||
|
||||
if (fieldMetadata.type === FieldMetadataType.RELATION) {
|
||||
throw new ValidationError(
|
||||
"Relation fields can't be deleted, you need to delete the RelationMetadata instead",
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.fieldMetadataService.deleteOneField(input, workspaceId);
|
||||
} catch (error) {
|
||||
|
||||
@ -10,6 +10,8 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { DataSource, FindOneOptions, In, Repository } from 'typeorm';
|
||||
import { v4 as uuidV4, v4 } from 'uuid';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { settings } from 'src/engine/constants/settings';
|
||||
import { generateMessageId } from 'src/engine/core-modules/i18n/utils/generateMessageId';
|
||||
@ -81,6 +83,8 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
@InjectRepository(RelationMetadataEntity, 'metadata')
|
||||
private readonly relationMetadataRepository: Repository<RelationMetadataEntity>,
|
||||
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
|
||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
||||
@ -350,9 +354,48 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
fieldMetadataId: fieldMetadata.id,
|
||||
});
|
||||
|
||||
await fieldMetadataRepository.delete(fieldMetadata.id);
|
||||
if (fieldMetadata.type === FieldMetadataType.RELATION) {
|
||||
const isManyToManyRelation =
|
||||
(fieldMetadata as FieldMetadataEntity<FieldMetadataType.RELATION>)
|
||||
.settings?.relationType === RelationType.MANY_TO_ONE;
|
||||
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
const targetFieldMetadata =
|
||||
await this.fieldMetadataRepository.findOneBy({
|
||||
id: fieldMetadata.relationTargetFieldMetadataId,
|
||||
});
|
||||
|
||||
if (targetFieldMetadata) {
|
||||
await this.relationMetadataRepository.delete({
|
||||
fromFieldMetadataId: In([fieldMetadata.id, targetFieldMetadata.id]),
|
||||
});
|
||||
await this.relationMetadataRepository.delete({
|
||||
toFieldMetadataId: In([fieldMetadata.id, targetFieldMetadata.id]),
|
||||
});
|
||||
await fieldMetadataRepository.delete({
|
||||
id: In([fieldMetadata.id, targetFieldMetadata.id]),
|
||||
});
|
||||
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(`delete-${fieldMetadata.name}`),
|
||||
workspaceId,
|
||||
[
|
||||
{
|
||||
name: computeObjectTargetTable(objectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.DROP,
|
||||
columnName: isManyToManyRelation
|
||||
? `${(fieldMetadata as FieldMetadataEntity<FieldMetadataType.RELATION>).settings?.joinColumnName}`
|
||||
: `${(targetFieldMetadata as FieldMetadataEntity<FieldMetadataType.RELATION>).settings?.joinColumnName}`,
|
||||
} satisfies WorkspaceMigrationColumnDrop,
|
||||
],
|
||||
} satisfies WorkspaceMigrationTableAction,
|
||||
],
|
||||
);
|
||||
}
|
||||
} else if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
await fieldMetadataRepository.delete(fieldMetadata.id);
|
||||
const compositeType = compositeTypeDefinitions.get(fieldMetadata.type);
|
||||
|
||||
if (!compositeType) {
|
||||
@ -383,6 +426,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
],
|
||||
);
|
||||
} else {
|
||||
await fieldMetadataRepository.delete(fieldMetadata.id);
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(`delete-${fieldMetadata.name}`),
|
||||
workspaceId,
|
||||
|
||||
@ -310,17 +310,17 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
|
||||
const updatedObject = await super.updateOne(inputId, inputPayload);
|
||||
|
||||
const isNewRelationEnabled = await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsNewRelationEnabled,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
await this.handleObjectNameAndLabelUpdates(
|
||||
existingObjectMetadata,
|
||||
existingObjectMetadataCombinedWithUpdateInput,
|
||||
inputPayload,
|
||||
);
|
||||
|
||||
const isNewRelationEnabled = await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsNewRelationEnabled,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (inputPayload.isActive !== undefined) {
|
||||
// For new relation system, the active status is stitched to the field metadata
|
||||
if (!isNewRelationEnabled) {
|
||||
|
||||
@ -6,6 +6,7 @@ import { capitalize } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
|
||||
import { FieldMetadataDefaultSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
@ -165,15 +166,30 @@ export class ObjectMetadataFieldRelationService {
|
||||
objectMetadataId: targetObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
});
|
||||
|
||||
const isTargetFieldMetadataManyToOneRelation =
|
||||
(
|
||||
targetFieldMetadataToUpdate as FieldMetadataEntity<FieldMetadataType.RELATION>
|
||||
).settings?.relationType === RelationType.MANY_TO_ONE;
|
||||
|
||||
const targetFieldMetadata = await this.fieldMetadataRepository.save({
|
||||
id: targetFieldMetadataToUpdate.id,
|
||||
...targetFieldMetadataUpdateData,
|
||||
settings: {
|
||||
...(targetFieldMetadataToUpdate.settings as FieldMetadataDefaultSettings),
|
||||
...(isTargetFieldMetadataManyToOneRelation
|
||||
? {
|
||||
joinColumnName: `${sourceObjectMetadata.nameSingular}Id`,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
const sourceFieldMetadataUpdateData = this.updateSourceFieldMetadata(
|
||||
sourceObjectMetadata,
|
||||
targetObjectMetadata,
|
||||
);
|
||||
|
||||
const sourceFieldMetadataToUpdate =
|
||||
await this.fieldMetadataRepository.findOneByOrFail({
|
||||
standardId:
|
||||
@ -181,9 +197,23 @@ export class ObjectMetadataFieldRelationService {
|
||||
objectMetadataId: sourceObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
});
|
||||
|
||||
const isSourceFieldMetadataManyToOneRelation =
|
||||
(
|
||||
sourceFieldMetadataToUpdate as FieldMetadataEntity<FieldMetadataType.RELATION>
|
||||
).settings?.relationType === RelationType.MANY_TO_ONE;
|
||||
|
||||
const sourceFieldMetadata = await this.fieldMetadataRepository.save({
|
||||
id: sourceFieldMetadataToUpdate.id,
|
||||
...sourceFieldMetadataUpdateData,
|
||||
settings: {
|
||||
...(sourceFieldMetadataToUpdate.settings as FieldMetadataDefaultSettings),
|
||||
...(isSourceFieldMetadataManyToOneRelation
|
||||
? {
|
||||
joinColumnName: `${targetObjectMetadata.nameSingular}Id`,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@ -276,7 +276,7 @@ export class ObjectMetadataMigrationService {
|
||||
relationToDelete.toFieldMetadataId,
|
||||
]);
|
||||
|
||||
if (relationToDelete.direction === 'from') {
|
||||
if (relationToDelete.direction === 'from' && !isNewRelationEnabled) {
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(
|
||||
`delete-${RELATION_MIGRATION_PRIORITY_PREFIX}-${relationToDelete.fromObjectName}-${relationToDelete.toObjectName}`,
|
||||
|
||||
@ -54,6 +54,7 @@ describe('SearchResolver', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const listingObjectMetadata = objectsMetadata.find(
|
||||
(object) => object.nameSingular === LISTING_NAME_SINGULAR,
|
||||
);
|
||||
|
||||
@ -4,7 +4,6 @@ import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object
|
||||
import { findManyObjectMetadataQueryFactory } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata-query-factory.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { createOneRelationMetadataFactory } from 'test/integration/metadata/suites/utils/create-one-relation-metadata-factory.util';
|
||||
import { deleteOneRelationMetadataItemFactory } from 'test/integration/metadata/suites/utils/delete-one-relation-metadata-factory.util';
|
||||
import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
@ -14,7 +13,6 @@ const LISTING_NAME_SINGULAR = 'listing';
|
||||
|
||||
describe('Custom object renaming', () => {
|
||||
let listingObjectId = '';
|
||||
let customRelationId = '';
|
||||
|
||||
const STANDARD_OBJECT_RELATIONS = [
|
||||
'noteTarget',
|
||||
@ -108,10 +106,6 @@ describe('Custom object renaming', () => {
|
||||
|
||||
const fields = await makeMetadataAPIRequest(fieldsGraphqlOperation);
|
||||
|
||||
const foreignKeyFieldsMetadataForListing = fields.body.data.fields.edges
|
||||
.filter((field) => field.node.name === `${LISTING_NAME_SINGULAR}Id`)
|
||||
.map((field) => field.node);
|
||||
|
||||
const relationFieldsMetadataForListing = fields.body.data.fields.edges
|
||||
.filter(
|
||||
(field) =>
|
||||
@ -120,21 +114,7 @@ describe('Custom object renaming', () => {
|
||||
)
|
||||
.map((field) => field.node);
|
||||
|
||||
expect(foreignKeyFieldsMetadataForListing.length).toBe(5);
|
||||
|
||||
STANDARD_OBJECT_RELATIONS.forEach((relation) => {
|
||||
// foreignKey field
|
||||
const foreignKeyFieldMetadataId = foreignKeyFieldsMetadataForListing.find(
|
||||
(field) =>
|
||||
field.object.id ===
|
||||
standardObjectRelationsMap[relation].objectMetadataId,
|
||||
).id;
|
||||
|
||||
expect(foreignKeyFieldMetadataId).not.toBeUndefined();
|
||||
|
||||
standardObjectRelationsMap[relation].foreignKeyFieldMetadataId =
|
||||
foreignKeyFieldMetadataId;
|
||||
|
||||
// relation field
|
||||
const relationFieldMetadataId = relationFieldsMetadataForListing.find(
|
||||
(field) =>
|
||||
@ -189,8 +169,6 @@ describe('Custom object renaming', () => {
|
||||
);
|
||||
|
||||
// Assert
|
||||
customRelationId = relationResponse.body.data.createOneRelationMetadata.id;
|
||||
|
||||
relationFieldMetadataOnPersonId =
|
||||
relationResponse.body.data.createOneRelationMetadata.fromFieldMetadataId;
|
||||
});
|
||||
@ -233,29 +211,8 @@ describe('Custom object renaming', () => {
|
||||
(field) => field.node,
|
||||
);
|
||||
|
||||
expect(
|
||||
fieldsMetadata.find(
|
||||
(field) => field.name === `${LISTING_NAME_SINGULAR}Id`,
|
||||
),
|
||||
).toBeUndefined();
|
||||
|
||||
// standard relations have been updated
|
||||
STANDARD_OBJECT_RELATIONS.forEach((relation) => {
|
||||
// foreignKey field
|
||||
const foreignKeyFieldMetadataId =
|
||||
standardObjectRelationsMap[relation].foreignKeyFieldMetadataId;
|
||||
|
||||
const updatedForeignKeyFieldMetadata = fieldsMetadata.find(
|
||||
(field) => field.id === foreignKeyFieldMetadataId,
|
||||
);
|
||||
|
||||
expect(updatedForeignKeyFieldMetadata.name).toBe(
|
||||
`${HOUSE_NAME_SINGULAR}Id`,
|
||||
);
|
||||
expect(updatedForeignKeyFieldMetadata.label).toBe(
|
||||
'House ID (foreign key)',
|
||||
);
|
||||
|
||||
// relation field
|
||||
const relationFieldMetadataId =
|
||||
standardObjectRelationsMap[relation].relationFieldMetadataId;
|
||||
@ -276,19 +233,7 @@ describe('Custom object renaming', () => {
|
||||
expect(updatedRelationFieldMetadata.name).toBe(RELATION_FROM_NAME);
|
||||
});
|
||||
|
||||
it('4. should delete custom relation', async () => {
|
||||
const graphqlOperation = deleteOneRelationMetadataItemFactory({
|
||||
idToDelete: customRelationId,
|
||||
});
|
||||
|
||||
const response = await makeMetadataAPIRequest(graphqlOperation);
|
||||
|
||||
const deleteRelationResponse = response.body.data.deleteOneRelation;
|
||||
|
||||
expect(deleteRelationResponse.id).toBe(customRelationId);
|
||||
});
|
||||
|
||||
it('5. should delete custom object', async () => {
|
||||
it('4. should delete custom object', async () => {
|
||||
const { data } = await deleteOneObjectMetadata({
|
||||
input: {
|
||||
idToDelete: listingObjectId,
|
||||
|
||||
@ -52921,6 +52921,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"storybook-addon-mock-date@npm:^0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "storybook-addon-mock-date@npm:0.6.0"
|
||||
checksum: 10c0/24c48d65a04eb1931e39495fe42692cea428db4b66874db86ab4e9e6f99cd0bba56cd25c56c9a56b42e53a14edfd38f79bcdef4c1f0e2758096528863d32d408
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"storybook-addon-pseudo-states@npm:^2.1.2":
|
||||
version: 2.2.1
|
||||
resolution: "storybook-addon-pseudo-states@npm:2.2.1"
|
||||
@ -55501,6 +55508,7 @@ __metadata:
|
||||
source-map-support: "npm:^0.5.20"
|
||||
storybook: "npm:^7.6.3"
|
||||
storybook-addon-cookie: "npm:^3.2.0"
|
||||
storybook-addon-mock-date: "npm:^0.6.0"
|
||||
storybook-addon-pseudo-states: "npm:^2.1.2"
|
||||
storybook-dark-mode: "npm:^3.0.3"
|
||||
stripe: "npm:^17.3.1"
|
||||
|
||||
Reference in New Issue
Block a user