Force explicit deletion behavior for relations (#4775)

We've seen a few cascading errors (e.g. comment.activityId would be non
nullable but cascade behavior is set to "set null"). I think it's safer
if we have to explicitly chose the deletion behavior it every time.

Especially since Postgres default to "No action" while we defaulted to
"Set Null", which is confusing.

In the future we will most likely introduce a second param
`onSoftDelete` in the decorator
This commit is contained in:
Félix Malfait
2024-04-03 18:30:12 +02:00
committed by GitHub
parent ccd02fe58c
commit b65d82c274
9 changed files with 31 additions and 9 deletions

View File

@ -3,10 +3,6 @@ import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-met
export const convertOnDeleteActionToOnDelete = (
onDeleteAction: RelationOnDeleteAction | undefined,
): 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION' | undefined => {
if (!onDeleteAction) {
return 'SET NULL';
}
switch (onDeleteAction) {
case 'CASCADE':
return 'CASCADE';

View File

@ -6,7 +6,6 @@ import {
} from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/reflect-relation-metadata.interface';
import { TypedReflect } from 'src/utils/typed-reflect';
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
export function RelationMetadata<TClass extends object>(
params: RelationMetadataDecoratorParams<TClass>,
@ -27,7 +26,6 @@ export function RelationMetadata<TClass extends object>(
target,
fieldKey,
...params,
onDelete: params.onDelete ?? RelationOnDeleteAction.SET_NULL,
gate,
} satisfies ReflectRelationMetadata,
],

View File

@ -11,7 +11,7 @@ export interface RelationMetadataDecoratorParams<T> {
type: RelationMetadataType;
inverseSideTarget: () => ObjectType<T>;
inverseSideFieldKey?: keyof T;
onDelete?: RelationOnDeleteAction;
onDelete: RelationOnDeleteAction;
}
export interface ReflectRelationMetadata

View File

@ -1,5 +1,8 @@
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import {
RelationMetadataType,
RelationOnDeleteAction,
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { activityStandardFieldIds } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { standardObjectIds } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
import { FieldMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/decorators/field-metadata.decorator';
@ -91,6 +94,7 @@ export class ActivityObjectMetadata extends BaseObjectMetadata {
@RelationMetadata({
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => ActivityTargetObjectMetadata,
onDelete: RelationOnDeleteAction.SET_NULL,
})
@IsNullable()
activityTargets: ActivityTargetObjectMetadata[];
@ -105,6 +109,7 @@ export class ActivityObjectMetadata extends BaseObjectMetadata {
@RelationMetadata({
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => AttachmentObjectMetadata,
onDelete: RelationOnDeleteAction.SET_NULL,
})
@IsNullable()
attachments: AttachmentObjectMetadata[];
@ -119,6 +124,7 @@ export class ActivityObjectMetadata extends BaseObjectMetadata {
@RelationMetadata({
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => CommentObjectMetadata,
onDelete: RelationOnDeleteAction.CASCADE,
})
@IsNullable()
comments: CommentObjectMetadata[];
@ -131,6 +137,7 @@ export class ActivityObjectMetadata extends BaseObjectMetadata {
icon: 'IconUserCircle',
joinColumn: 'authorId',
})
@IsNullable()
author: WorkspaceMemberObjectMetadata;
@FieldMetadata({

View File

@ -132,6 +132,7 @@ export class CompanyObjectMetadata extends BaseObjectMetadata {
@RelationMetadata({
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => PersonObjectMetadata,
onDelete: RelationOnDeleteAction.SET_NULL,
})
@IsNullable()
people: PersonObjectMetadata[];
@ -173,6 +174,7 @@ export class CompanyObjectMetadata extends BaseObjectMetadata {
@RelationMetadata({
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => OpportunityObjectMetadata,
onDelete: RelationOnDeleteAction.SET_NULL,
})
@IsNullable()
opportunities: OpportunityObjectMetadata[];

View File

@ -163,6 +163,7 @@ export class OpportunityObjectMetadata extends BaseObjectMetadata {
@RelationMetadata({
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => AttachmentObjectMetadata,
onDelete: RelationOnDeleteAction.CASCADE,
})
@IsNullable()
attachments: AttachmentObjectMetadata[];
@ -177,6 +178,7 @@ export class OpportunityObjectMetadata extends BaseObjectMetadata {
@RelationMetadata({
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => EventObjectMetadata,
onDelete: RelationOnDeleteAction.SET_NULL,
})
@IsNullable()
events: EventObjectMetadata[];

View File

@ -142,6 +142,7 @@ export class PersonObjectMetadata extends BaseObjectMetadata {
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => OpportunityObjectMetadata,
inverseSideFieldKey: 'pointOfContact',
onDelete: RelationOnDeleteAction.SET_NULL,
})
pointOfContactForOpportunities: OpportunityObjectMetadata[];
@ -199,6 +200,7 @@ export class PersonObjectMetadata extends BaseObjectMetadata {
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => MessageParticipantObjectMetadata,
inverseSideFieldKey: 'person',
onDelete: RelationOnDeleteAction.SET_NULL,
})
@IsSystem()
messageParticipants: MessageParticipantObjectMetadata[];
@ -213,6 +215,7 @@ export class PersonObjectMetadata extends BaseObjectMetadata {
@RelationMetadata({
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => CalendarEventAttendeeObjectMetadata,
onDelete: RelationOnDeleteAction.SET_NULL,
})
@Gate({
featureFlag: 'IS_CALENDAR_ENABLED',

View File

@ -1,5 +1,8 @@
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import {
RelationMetadataType,
RelationOnDeleteAction,
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { viewStandardFieldIds } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { standardObjectIds } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
import { FieldMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/decorators/field-metadata.decorator';
@ -102,6 +105,7 @@ export class ViewObjectMetadata extends BaseObjectMetadata {
@RelationMetadata({
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => ViewFieldObjectMetadata,
onDelete: RelationOnDeleteAction.SET_NULL,
})
@IsNullable()
viewFields: ViewFieldObjectMetadata[];
@ -116,6 +120,7 @@ export class ViewObjectMetadata extends BaseObjectMetadata {
@RelationMetadata({
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => ViewFilterObjectMetadata,
onDelete: RelationOnDeleteAction.SET_NULL,
})
@IsNullable()
viewFilters: ViewFilterObjectMetadata[];
@ -130,6 +135,7 @@ export class ViewObjectMetadata extends BaseObjectMetadata {
@RelationMetadata({
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => ViewSortObjectMetadata,
onDelete: RelationOnDeleteAction.SET_NULL,
})
@IsNullable()
viewSorts: ViewSortObjectMetadata[];

View File

@ -102,6 +102,7 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => ActivityObjectMetadata,
inverseSideFieldKey: 'author',
onDelete: RelationOnDeleteAction.SET_NULL,
})
authoredActivities: ActivityObjectMetadata[];
@ -116,6 +117,7 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => ActivityObjectMetadata,
inverseSideFieldKey: 'assignee',
onDelete: RelationOnDeleteAction.SET_NULL,
})
assignedActivities: ActivityObjectMetadata[];
@ -144,6 +146,7 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => CompanyObjectMetadata,
inverseSideFieldKey: 'accountOwner',
onDelete: RelationOnDeleteAction.SET_NULL,
})
accountOwnerForCompanies: CompanyObjectMetadata[];
@ -158,6 +161,7 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => AttachmentObjectMetadata,
inverseSideFieldKey: 'author',
onDelete: RelationOnDeleteAction.SET_NULL,
})
authoredAttachments: AttachmentObjectMetadata[];
@ -172,6 +176,7 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => CommentObjectMetadata,
inverseSideFieldKey: 'author',
onDelete: RelationOnDeleteAction.SET_NULL,
})
authoredComments: CommentObjectMetadata[];
@ -201,6 +206,7 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => MessageParticipantObjectMetadata,
inverseSideFieldKey: 'workspaceMember',
onDelete: RelationOnDeleteAction.SET_NULL,
})
messageParticipants: MessageParticipantObjectMetadata[];
@ -215,6 +221,7 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => BlocklistObjectMetadata,
inverseSideFieldKey: 'workspaceMember',
onDelete: RelationOnDeleteAction.SET_NULL,
})
blocklist: BlocklistObjectMetadata[];
@ -229,6 +236,7 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => CalendarEventAttendeeObjectMetadata,
inverseSideFieldKey: 'workspaceMember',
onDelete: RelationOnDeleteAction.SET_NULL,
})
@Gate({
featureFlag: 'IS_CALENDAR_ENABLED',