Support custom composite field deletion (#6320)
as per title Fixes #6033 and closes #4841
This commit is contained in:
@ -21,7 +21,6 @@ export const SettingsObjectFieldInactiveActionDropdown = ({
|
|||||||
scopeKey,
|
scopeKey,
|
||||||
onDelete,
|
onDelete,
|
||||||
isCustomField,
|
isCustomField,
|
||||||
fieldType,
|
|
||||||
}: SettingsObjectFieldInactiveActionDropdownProps) => {
|
}: SettingsObjectFieldInactiveActionDropdownProps) => {
|
||||||
const dropdownId = `${scopeKey}-settings-field-disabled-action-dropdown`;
|
const dropdownId = `${scopeKey}-settings-field-disabled-action-dropdown`;
|
||||||
|
|
||||||
@ -37,7 +36,7 @@ export const SettingsObjectFieldInactiveActionDropdown = ({
|
|||||||
closeDropdown();
|
closeDropdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
const isDeletable = isCustomField && fieldType !== FieldMetadataType.Address;
|
const isDeletable = isCustomField;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import styled from '@emotion/styled';
|
|
||||||
import { H2Title, IconPlus, IconSettings } from 'twenty-ui';
|
import { H2Title, IconPlus, IconSettings } from 'twenty-ui';
|
||||||
|
|
||||||
import { LABEL_IDENTIFIER_FIELD_METADATA_TYPES } from '@/object-metadata/constants/LabelIdentifierFieldMetadataTypes';
|
import { LABEL_IDENTIFIER_FIELD_METADATA_TYPES } from '@/object-metadata/constants/LabelIdentifierFieldMetadataTypes';
|
||||||
@ -198,7 +198,6 @@ export const SettingsObjectDetail = () => {
|
|||||||
ActionIcon={
|
ActionIcon={
|
||||||
<SettingsObjectFieldInactiveActionDropdown
|
<SettingsObjectFieldInactiveActionDropdown
|
||||||
isCustomField={!!deactivatedMetadataField.isCustom}
|
isCustomField={!!deactivatedMetadataField.isCustom}
|
||||||
fieldType={deactivatedMetadataField.type}
|
|
||||||
scopeKey={deactivatedMetadataField.id}
|
scopeKey={deactivatedMetadataField.id}
|
||||||
onActivate={() =>
|
onActivate={() =>
|
||||||
activateMetadataField(deactivatedMetadataField)
|
activateMetadataField(deactivatedMetadataField)
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { v4 as uuidV4 } from 'uuid';
|
|||||||
|
|
||||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||||
|
import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||||
import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||||
import { DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input';
|
import { DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input';
|
||||||
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||||
@ -20,8 +21,12 @@ import {
|
|||||||
FieldMetadataExceptionCode,
|
FieldMetadataExceptionCode,
|
||||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||||
import { assertDoesNotNullifyDefaultValueForNonNullableField } from 'src/engine/metadata-modules/field-metadata/utils/assert-does-not-nullify-default-value-for-non-nullable-field.util';
|
import { assertDoesNotNullifyDefaultValueForNonNullableField } from 'src/engine/metadata-modules/field-metadata/utils/assert-does-not-nullify-default-value-for-non-nullable-field.util';
|
||||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
import {
|
||||||
|
computeColumnName,
|
||||||
|
computeCompositeColumnName,
|
||||||
|
} from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||||
import { generateNullable } from 'src/engine/metadata-modules/field-metadata/utils/generate-nullable';
|
import { generateNullable } from 'src/engine/metadata-modules/field-metadata/utils/generate-nullable';
|
||||||
|
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||||
import {
|
import {
|
||||||
@ -460,22 +465,54 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
|
|
||||||
await fieldMetadataRepository.delete(fieldMetadata.id);
|
await fieldMetadataRepository.delete(fieldMetadata.id);
|
||||||
|
|
||||||
await this.workspaceMigrationService.createCustomMigration(
|
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||||
generateMigrationName(`delete-${fieldMetadata.name}`),
|
const compositeType = compositeTypeDefintions.get(fieldMetadata.type);
|
||||||
workspaceId,
|
|
||||||
[
|
if (!compositeType) {
|
||||||
{
|
throw new Error(
|
||||||
name: computeObjectTargetTable(objectMetadata),
|
`Composite type not found for field metadata type: ${fieldMetadata.type}`,
|
||||||
action: WorkspaceMigrationTableActionType.ALTER,
|
);
|
||||||
columns: [
|
}
|
||||||
{
|
|
||||||
action: WorkspaceMigrationColumnActionType.DROP,
|
await this.workspaceMigrationService.createCustomMigration(
|
||||||
columnName: computeColumnName(fieldMetadata),
|
generateMigrationName(
|
||||||
} satisfies WorkspaceMigrationColumnDrop,
|
`delete-${fieldMetadata.name}-composite-columns`,
|
||||||
],
|
),
|
||||||
} satisfies WorkspaceMigrationTableAction,
|
workspaceId,
|
||||||
],
|
[
|
||||||
);
|
{
|
||||||
|
name: computeObjectTargetTable(objectMetadata),
|
||||||
|
action: WorkspaceMigrationTableActionType.ALTER,
|
||||||
|
columns: compositeType.properties.map((property) => {
|
||||||
|
return {
|
||||||
|
action: WorkspaceMigrationColumnActionType.DROP,
|
||||||
|
columnName: computeCompositeColumnName(
|
||||||
|
fieldMetadata.name,
|
||||||
|
property,
|
||||||
|
),
|
||||||
|
} satisfies WorkspaceMigrationColumnDrop;
|
||||||
|
}),
|
||||||
|
} satisfies WorkspaceMigrationTableAction,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await this.workspaceMigrationService.createCustomMigration(
|
||||||
|
generateMigrationName(`delete-${fieldMetadata.name}`),
|
||||||
|
workspaceId,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: computeObjectTargetTable(objectMetadata),
|
||||||
|
action: WorkspaceMigrationTableActionType.ALTER,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
action: WorkspaceMigrationColumnActionType.DROP,
|
||||||
|
columnName: computeColumnName(fieldMetadata),
|
||||||
|
} satisfies WorkspaceMigrationColumnDrop,
|
||||||
|
],
|
||||||
|
} satisfies WorkspaceMigrationTableAction,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
|
||||||
import { CompositeProperty } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface';
|
import { CompositeProperty } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface';
|
||||||
|
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||||
|
|
||||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
|
||||||
import { pascalCase } from 'src/utils/pascal-case';
|
|
||||||
import {
|
import {
|
||||||
FieldMetadataException,
|
FieldMetadataException,
|
||||||
FieldMetadataExceptionCode,
|
FieldMetadataExceptionCode,
|
||||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||||
|
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||||
|
import { pascalCase } from 'src/utils/pascal-case';
|
||||||
|
|
||||||
type ComputeColumnNameOptions = { isForeignKey?: boolean };
|
type ComputeColumnNameOptions = { isForeignKey?: boolean };
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ export function computeColumnName<T extends FieldMetadataType | 'default'>(
|
|||||||
|
|
||||||
if (isCompositeFieldMetadataType(fieldMetadataOrFieldName.type)) {
|
if (isCompositeFieldMetadataType(fieldMetadataOrFieldName.type)) {
|
||||||
throw new FieldMetadataException(
|
throw new FieldMetadataException(
|
||||||
`Cannot compute composite column name for non-composite field metadata type: ${fieldMetadataOrFieldName.type}`,
|
`Cannot compute composite column name for field: ${fieldMetadataOrFieldName.type}`,
|
||||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user