mirror of
https://github.com/lingble/twenty.git
synced 2025-10-29 11:52:28 +00:00
Remove objectMetadata isSoftDeletable
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class RemoveObjectMetadataIsSoftDeletable1726486735275
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'RemoveObjectMetadataIsSoftDeletable1726486735275';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."objectMetadata" DROP COLUMN "isSoftDeletable"`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."objectMetadata" ADD "isSoftDeletable" boolean`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,6 @@ export class GraphqlQueryParser {
|
||||
|
||||
parseFilter(
|
||||
recordFilter: RecordFilter,
|
||||
shouldAddDefaultSoftDeleteCondition = false,
|
||||
): FindOptionsWhere<ObjectLiteral> | FindOptionsWhere<ObjectLiteral>[] {
|
||||
const graphqlQueryFilterParser = new GraphqlQueryFilterParser(
|
||||
this.fieldMetadataMap,
|
||||
@@ -41,10 +40,7 @@ export class GraphqlQueryParser {
|
||||
|
||||
const parsedFilter = graphqlQueryFilterParser.parse(recordFilter);
|
||||
|
||||
if (
|
||||
!shouldAddDefaultSoftDeleteCondition ||
|
||||
!('deletedAt' in this.fieldMetadataMap)
|
||||
) {
|
||||
if (!('deletedAt' in this.fieldMetadataMap)) {
|
||||
return parsedFilter;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,10 +70,7 @@ export class GraphqlQueryFindManyResolverService {
|
||||
args.orderBy ?? [],
|
||||
isForwardPagination,
|
||||
);
|
||||
const where = graphqlQueryParser.parseFilter(
|
||||
args.filter ?? ({} as Filter),
|
||||
objectMetadataItem.isSoftDeletable ?? false,
|
||||
);
|
||||
const where = graphqlQueryParser.parseFilter(args.filter ?? ({} as Filter));
|
||||
|
||||
const cursor = this.getCursor(args);
|
||||
const limit = args.first ?? args.last ?? QUERY_MAX_RECORDS;
|
||||
|
||||
@@ -55,10 +55,7 @@ export class GraphqlQueryFindOneResolverService {
|
||||
objectMetadataItem,
|
||||
graphqlFields(info),
|
||||
);
|
||||
const where = graphqlQueryParser.parseFilter(
|
||||
args.filter ?? ({} as Filter),
|
||||
objectMetadataItem.isSoftDeletable ?? false,
|
||||
);
|
||||
const where = graphqlQueryParser.parseFilter(args.filter ?? ({} as Filter));
|
||||
|
||||
const objectRecord = await repository.findOne({ where, select, relations });
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export class FindManyQueryFactory {
|
||||
const argsString = this.argsStringFactory.create(
|
||||
args,
|
||||
options.fieldMetadataCollection,
|
||||
!options.withSoftDeleted && !!options.objectMetadataItem.isSoftDeletable,
|
||||
!options.withSoftDeleted,
|
||||
);
|
||||
|
||||
return `
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceQueryBuilderOptions } from 'src/engine/api/graphql/workspace-query-builder/interfaces/workspace-query-builder-options.interface';
|
||||
import { RecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
|
||||
import { WorkspaceQueryBuilderOptions } from 'src/engine/api/graphql/workspace-query-builder/interfaces/workspace-query-builder-options.interface';
|
||||
import { FindOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
@@ -29,7 +29,7 @@ export class FindOneQueryFactory {
|
||||
const argsString = this.argsStringFactory.create(
|
||||
args,
|
||||
options.fieldMetadataCollection,
|
||||
!options.withSoftDeleted && !!options.objectMetadataItem.isSoftDeletable,
|
||||
!options.withSoftDeleted,
|
||||
);
|
||||
|
||||
return `
|
||||
|
||||
@@ -5,18 +5,18 @@ import { GraphQLResolveInfo } from 'graphql';
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||
import { getFieldArgumentsByKey } from 'src/engine/api/graphql/workspace-query-builder/utils/get-field-arguments-by-key.util';
|
||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import {
|
||||
deduceRelationDirection,
|
||||
RelationDirection,
|
||||
} from 'src/engine/utils/deduce-relation-direction.util';
|
||||
import { getFieldArgumentsByKey } from 'src/engine/api/graphql/workspace-query-builder/utils/get-field-arguments-by-key.util';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||
|
||||
import { FieldsStringFactory } from './fields-string.factory';
|
||||
import { ArgsStringFactory } from './args-string.factory';
|
||||
import { FieldsStringFactory } from './fields-string.factory';
|
||||
|
||||
@Injectable()
|
||||
export class RelationFieldAliasFactory {
|
||||
@@ -101,7 +101,7 @@ export class RelationFieldAliasFactory {
|
||||
const argsString = this.argsStringFactory.create(
|
||||
args,
|
||||
referencedObjectMetadata.fields ?? [],
|
||||
!withSoftDeleted && !!referencedObjectMetadata.isSoftDeletable,
|
||||
!withSoftDeleted,
|
||||
);
|
||||
const fieldsString =
|
||||
await this.fieldsStringFactory.createFieldsStringRecursive(
|
||||
|
||||
@@ -548,7 +548,6 @@ export class WorkspaceQueryRunnerService {
|
||||
options: WorkspaceQueryRunnerOptions,
|
||||
): Promise<Record[] | undefined> {
|
||||
const { authContext, objectMetadataItem } = options;
|
||||
let query: string;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||
|
||||
@@ -564,25 +563,18 @@ export class WorkspaceQueryRunnerService {
|
||||
args,
|
||||
);
|
||||
|
||||
if (objectMetadataItem.isSoftDeletable) {
|
||||
query = await this.workspaceQueryBuilderFactory.updateMany(
|
||||
{
|
||||
filter: hookedArgs.filter,
|
||||
data: {
|
||||
deletedAt: new Date().toISOString(),
|
||||
},
|
||||
const query = await this.workspaceQueryBuilderFactory.updateMany(
|
||||
{
|
||||
filter: hookedArgs.filter,
|
||||
data: {
|
||||
deletedAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
...options,
|
||||
atMost: maximumRecordAffected,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
query = await this.workspaceQueryBuilderFactory.deleteMany(hookedArgs, {
|
||||
},
|
||||
{
|
||||
...options,
|
||||
atMost: maximumRecordAffected,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const repository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
@@ -603,7 +595,7 @@ export class WorkspaceQueryRunnerService {
|
||||
await this.parseResult<PGGraphQLMutation<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
objectMetadataItem.isSoftDeletable ? 'update' : 'deleteFrom',
|
||||
'update',
|
||||
authContext.workspace.id,
|
||||
)
|
||||
)?.records;
|
||||
@@ -648,13 +640,6 @@ export class WorkspaceQueryRunnerService {
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||
|
||||
if (!objectMetadataItem.isSoftDeletable) {
|
||||
throw new WorkspaceQueryRunnerException(
|
||||
'This method is reserved to objects that can be soft-deleted, use delete instead',
|
||||
WorkspaceQueryRunnerExceptionCode.DATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const maximumRecordAffected = this.environmentService.get(
|
||||
'MUTATION_MAXIMUM_AFFECTED_RECORDS',
|
||||
);
|
||||
@@ -711,13 +696,6 @@ export class WorkspaceQueryRunnerService {
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||
|
||||
if (!objectMetadataItem.isSoftDeletable) {
|
||||
throw new WorkspaceQueryRunnerException(
|
||||
'This method is reserved to objects that can be soft-deleted',
|
||||
WorkspaceQueryRunnerExceptionCode.DATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const maximumRecordAffected = this.environmentService.get(
|
||||
'MUTATION_MAXIMUM_AFFECTED_RECORDS',
|
||||
);
|
||||
@@ -792,7 +770,6 @@ export class WorkspaceQueryRunnerService {
|
||||
authContext.workspace.id,
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
let query: string;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||
assertIsValidUuid(args.id);
|
||||
@@ -805,22 +782,15 @@ export class WorkspaceQueryRunnerService {
|
||||
args,
|
||||
);
|
||||
|
||||
if (objectMetadataItem.isSoftDeletable) {
|
||||
query = await this.workspaceQueryBuilderFactory.updateOne(
|
||||
{
|
||||
id: hookedArgs.id,
|
||||
data: {
|
||||
deletedAt: new Date().toISOString(),
|
||||
},
|
||||
const query = await this.workspaceQueryBuilderFactory.updateOne(
|
||||
{
|
||||
id: hookedArgs.id,
|
||||
data: {
|
||||
deletedAt: new Date().toISOString(),
|
||||
},
|
||||
options,
|
||||
);
|
||||
} else {
|
||||
query = await this.workspaceQueryBuilderFactory.deleteOne(
|
||||
hookedArgs,
|
||||
options,
|
||||
);
|
||||
}
|
||||
},
|
||||
options,
|
||||
);
|
||||
|
||||
const existingRecord = await repository.findOne({
|
||||
where: { id: args.id },
|
||||
@@ -832,7 +802,7 @@ export class WorkspaceQueryRunnerService {
|
||||
await this.parseResult<PGGraphQLMutation<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
objectMetadataItem.isSoftDeletable ? 'update' : 'deleteFrom',
|
||||
'update',
|
||||
authContext.workspace.id,
|
||||
)
|
||||
)?.records;
|
||||
|
||||
@@ -20,5 +20,4 @@ export interface ObjectMetadataInterface {
|
||||
isAuditLogged: boolean;
|
||||
labelIdentifierFieldMetadataId?: string | null;
|
||||
imageIdentifierFieldMetadataId?: string | null;
|
||||
isSoftDeletable?: boolean | null;
|
||||
}
|
||||
|
||||
@@ -69,9 +69,6 @@ export class ObjectMetadataEntity implements ObjectMetadataInterface {
|
||||
@Column({ default: true })
|
||||
isAuditLogged: boolean;
|
||||
|
||||
@Column({ nullable: true, type: 'boolean' })
|
||||
isSoftDeletable?: boolean | null;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
labelIdentifierFieldMetadataId?: string | null;
|
||||
|
||||
|
||||
@@ -215,7 +215,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
isCustom: isCustom,
|
||||
isSystem: false,
|
||||
isRemote: objectMetadataInput.isRemote,
|
||||
isSoftDeletable: true,
|
||||
fields: isCustom
|
||||
? // Creating default fields.
|
||||
// No need to create a custom migration for this though as the default columns are already
|
||||
|
||||
@@ -21,9 +21,7 @@ import { NoteTargetWorkspaceEntity } from 'src/modules/note/standard-objects/not
|
||||
import { TaskTargetWorkspaceEntity } from 'src/modules/task/standard-objects/task-target.workspace-entity';
|
||||
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
|
||||
|
||||
@WorkspaceCustomEntity({
|
||||
softDelete: true,
|
||||
})
|
||||
@WorkspaceCustomEntity()
|
||||
export class CustomWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceField({
|
||||
standardId: CUSTOM_OBJECT_STANDARD_FIELD_IDS.name,
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage';
|
||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||
|
||||
interface WorkspaceCustomEntityOptions {
|
||||
softDelete?: boolean;
|
||||
}
|
||||
|
||||
export function WorkspaceCustomEntity(
|
||||
options: WorkspaceCustomEntityOptions = {},
|
||||
): ClassDecorator {
|
||||
export function WorkspaceCustomEntity(options = {}): ClassDecorator {
|
||||
return (target) => {
|
||||
const gate = TypedReflect.getMetadata(
|
||||
'workspace:gate-metadata-args',
|
||||
@@ -17,7 +11,6 @@ export function WorkspaceCustomEntity(
|
||||
metadataArgsStorage.addExtendedEntities({
|
||||
target,
|
||||
gate,
|
||||
softDelete: options.softDelete,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ interface WorkspaceEntityOptions {
|
||||
icon?: string;
|
||||
labelIdentifierStandardId?: string;
|
||||
imageIdentifierStandardId?: string;
|
||||
softDelete?: boolean;
|
||||
}
|
||||
|
||||
export function WorkspaceEntity(
|
||||
@@ -48,7 +47,6 @@ export function WorkspaceEntity(
|
||||
isAuditLogged,
|
||||
isSystem,
|
||||
gate,
|
||||
softDelete: options.softDelete ?? true,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,10 +20,7 @@ type EntitySchemaColumnMap = {
|
||||
|
||||
@Injectable()
|
||||
export class EntitySchemaColumnFactory {
|
||||
create(
|
||||
fieldMetadataMap: FieldMetadataMap,
|
||||
softDelete: boolean,
|
||||
): EntitySchemaColumnMap {
|
||||
create(fieldMetadataMap: FieldMetadataMap): EntitySchemaColumnMap {
|
||||
let entitySchemaColumnMap: EntitySchemaColumnMap = {};
|
||||
|
||||
const fieldMetadataCollection = Object.values(fieldMetadataMap);
|
||||
@@ -31,11 +28,6 @@ export class EntitySchemaColumnFactory {
|
||||
for (const fieldMetadata of fieldMetadataCollection) {
|
||||
const key = fieldMetadata.name;
|
||||
|
||||
// Skip deletedAt column if soft delete is not enabled
|
||||
if (!softDelete && key === 'deletedAt') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
const relationMetadata =
|
||||
fieldMetadata.fromRelationMetadata ??
|
||||
|
||||
@@ -26,7 +26,6 @@ export class EntitySchemaFactory {
|
||||
): Promise<EntitySchema> {
|
||||
const columns = this.entitySchemaColumnFactory.create(
|
||||
objectMetadata.fields,
|
||||
objectMetadata.isSoftDeletable ?? false,
|
||||
);
|
||||
|
||||
const relations = await this.entitySchemaRelationFactory.create(
|
||||
|
||||
@@ -60,9 +60,4 @@ export interface WorkspaceEntityMetadataArgs {
|
||||
* Image identifier.
|
||||
*/
|
||||
readonly imageIdentifierStandardId: string | null;
|
||||
|
||||
/**
|
||||
* Enable soft delete.
|
||||
*/
|
||||
readonly softDelete: boolean;
|
||||
}
|
||||
|
||||
@@ -13,9 +13,4 @@ export interface WorkspaceExtendedEntityMetadataArgs {
|
||||
* Entity gate.
|
||||
*/
|
||||
readonly gate?: Gate;
|
||||
|
||||
/**
|
||||
* Enable soft delete.
|
||||
*/
|
||||
readonly softDelete?: boolean;
|
||||
}
|
||||
|
||||
@@ -150,14 +150,6 @@ export class StandardFieldFactory {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (
|
||||
workspaceFieldMetadataArgs.name === 'deletedAt' &&
|
||||
workspaceEntityMetadataArgs &&
|
||||
!workspaceEntityMetadataArgs.softDelete
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
type: workspaceFieldMetadataArgs.type,
|
||||
|
||||
@@ -54,7 +54,6 @@ export class StandardObjectFactory {
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isSystem: workspaceEntityMetadataArgs.isSystem ?? false,
|
||||
isSoftDeletable: workspaceEntityMetadataArgs.softDelete,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user