Reorganise calendar module (#6089)

Refactor Calendar into functional sub modules
<img width="437" alt="image"
src="https://github.com/twentyhq/twenty/assets/12035771/d9de3285-a226-4fe8-b3ef-2d8a21def2a5">

---------

Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
This commit is contained in:
Charles Bochet
2024-07-02 13:55:11 +02:00
committed by GitHub
parent ea7d52fba8
commit f8dd2cc733
66 changed files with 267 additions and 401 deletions

View File

@ -0,0 +1,52 @@
import { BadRequestException, NotFoundException, Scope } from '@nestjs/common';
import { FindManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import { CanAccessCalendarEventService } from 'src/modules/calendar/common/query-hooks/calendar-event/services/can-access-calendar-event.service';
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
@WorkspaceQueryHook({
key: `calendarEvent.findMany`,
scope: Scope.REQUEST,
})
export class CalendarEventFindManyPreQueryHook
implements WorkspaceQueryHookInstance
{
constructor(
@InjectWorkspaceRepository(CalendarChannelEventAssociationWorkspaceEntity)
private readonly calendarChannelEventAssociationRepository: WorkspaceRepository<CalendarChannelEventAssociationWorkspaceEntity>,
private readonly canAccessCalendarEventService: CanAccessCalendarEventService,
) {}
async execute(
userId: string,
workspaceId: string,
payload: FindManyResolverArgs,
): Promise<void> {
if (!payload?.filter?.id?.eq) {
throw new BadRequestException('id filter is required');
}
const calendarChannelCalendarEventAssociations =
await this.calendarChannelEventAssociationRepository.find({
where: {
calendarEventId: payload?.filter?.id?.eq,
},
relations: ['calendarChannel.connectedAccount'],
});
if (calendarChannelCalendarEventAssociations.length === 0) {
throw new NotFoundException();
}
await this.canAccessCalendarEventService.canAccessCalendarEvent(
userId,
workspaceId,
calendarChannelCalendarEventAssociations,
);
}
}

View File

@ -0,0 +1,53 @@
import { BadRequestException, NotFoundException, Scope } from '@nestjs/common';
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import { FindOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
import { CanAccessCalendarEventService } from 'src/modules/calendar/common/query-hooks/calendar-event/services/can-access-calendar-event.service';
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
@WorkspaceQueryHook({
key: `calendarEvent.findOne`,
scope: Scope.REQUEST,
})
export class CalendarEventFindOnePreQueryHook
implements WorkspaceQueryHookInstance
{
constructor(
@InjectWorkspaceRepository(CalendarChannelEventAssociationWorkspaceEntity)
private readonly calendarChannelEventAssociationRepository: WorkspaceRepository<CalendarChannelEventAssociationWorkspaceEntity>,
private readonly canAccessCalendarEventService: CanAccessCalendarEventService,
) {}
async execute(
userId: string,
workspaceId: string,
payload: FindOneResolverArgs,
): Promise<void> {
if (!payload?.filter?.id?.eq) {
throw new BadRequestException('id filter is required');
}
// TODO: Re-implement this using twenty ORM
const calendarChannelCalendarEventAssociations =
await this.calendarChannelEventAssociationRepository.find({
where: {
calendarEventId: payload?.filter?.id?.eq,
},
relations: ['calendarChannel.connectedAccount'],
});
if (calendarChannelCalendarEventAssociations.length === 0) {
throw new NotFoundException();
}
await this.canAccessCalendarEventService.canAccessCalendarEvent(
userId,
workspaceId,
calendarChannelCalendarEventAssociations,
);
}
}

View File

@ -0,0 +1,80 @@
import { ForbiddenException, Injectable } from '@nestjs/common';
import groupBy from 'lodash.groupby';
import { Any } from 'typeorm';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
import {
CalendarChannelWorkspaceEntity,
CalendarChannelVisibility,
} from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
@Injectable()
export class CanAccessCalendarEventService {
constructor(
@InjectWorkspaceRepository(CalendarChannelWorkspaceEntity)
private readonly calendarChannelRepository: WorkspaceRepository<CalendarChannelWorkspaceEntity>,
@InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity)
private readonly connectedAccountRepository: ConnectedAccountRepository,
@InjectObjectMetadataRepository(WorkspaceMemberWorkspaceEntity)
private readonly workspaceMemberService: WorkspaceMemberRepository,
) {}
public async canAccessCalendarEvent(
userId: string,
workspaceId: string,
calendarChannelCalendarEventAssociations: CalendarChannelEventAssociationWorkspaceEntity[],
) {
const calendarChannels = await this.calendarChannelRepository.find({
where: {
id: Any(
calendarChannelCalendarEventAssociations.map(
(association) => association.calendarChannel.id,
),
),
},
});
const calendarChannelsGroupByVisibility = groupBy(
calendarChannels,
(channel) => channel.visibility,
);
if (
calendarChannelsGroupByVisibility[
CalendarChannelVisibility.SHARE_EVERYTHING
]
) {
return;
}
const currentWorkspaceMember =
await this.workspaceMemberService.getByIdOrFail(userId, workspaceId);
const calendarChannelsConnectedAccounts =
await this.connectedAccountRepository.getByIds(
calendarChannels.map((channel) => channel.connectedAccountId),
workspaceId,
);
const calendarChannelsWorkspaceMemberIds =
calendarChannelsConnectedAccounts.map(
(connectedAccount) => connectedAccount.accountOwnerId,
);
if (
calendarChannelsWorkspaceMemberIds.includes(currentWorkspaceMember.id)
) {
return;
}
throw new ForbiddenException();
}
}

View File

@ -0,0 +1,30 @@
import { Module } from '@nestjs/common';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
import { CalendarEventFindManyPreQueryHook } from 'src/modules/calendar/common/query-hooks/calendar-event/calendar-event-find-many.pre-query.hook';
import { CalendarEventFindOnePreQueryHook } from 'src/modules/calendar/common/query-hooks/calendar-event/calendar-event-find-one.pre-query-hook';
import { CanAccessCalendarEventService } from 'src/modules/calendar/common/query-hooks/calendar-event/services/can-access-calendar-event.service';
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
@Module({
imports: [
TwentyORMModule.forFeature([
CalendarChannelEventAssociationWorkspaceEntity,
CalendarChannelWorkspaceEntity,
]),
ObjectMetadataRepositoryModule.forFeature([
ConnectedAccountWorkspaceEntity,
WorkspaceMemberWorkspaceEntity,
]),
],
providers: [
CanAccessCalendarEventService,
CalendarEventFindOnePreQueryHook,
CalendarEventFindManyPreQueryHook,
],
})
export class CalendarQueryHookModule {}

View File

@ -0,0 +1,67 @@
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { CALENDAR_CHANNEL_EVENT_ASSOCIATION_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator';
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
@WorkspaceEntity({
standardId: STANDARD_OBJECT_IDS.calendarChannelEventAssociation,
namePlural: 'calendarChannelEventAssociations',
labelSingular: 'Calendar Channel Event Association',
labelPlural: 'Calendar Channel Event Associations',
description: 'Calendar Channel Event Associations',
icon: 'IconCalendar',
})
@WorkspaceIsSystem()
@WorkspaceIsNotAuditLogged()
export class CalendarChannelEventAssociationWorkspaceEntity extends BaseWorkspaceEntity {
@WorkspaceField({
standardId:
CALENDAR_CHANNEL_EVENT_ASSOCIATION_STANDARD_FIELD_IDS.eventExternalId,
type: FieldMetadataType.TEXT,
label: 'Event external ID',
description: 'Event external ID',
icon: 'IconCalendar',
})
eventExternalId: string;
@WorkspaceRelation({
standardId:
CALENDAR_CHANNEL_EVENT_ASSOCIATION_STANDARD_FIELD_IDS.calendarChannel,
type: RelationMetadataType.MANY_TO_ONE,
label: 'Channel ID',
description: 'Channel ID',
icon: 'IconCalendar',
inverseSideTarget: () => CalendarChannelWorkspaceEntity,
inverseSideFieldKey: 'calendarChannelEventAssociations',
})
calendarChannel: Relation<CalendarChannelWorkspaceEntity>;
@WorkspaceJoinColumn('calendarChannel')
calendarChannelId: string;
@WorkspaceRelation({
standardId:
CALENDAR_CHANNEL_EVENT_ASSOCIATION_STANDARD_FIELD_IDS.calendarEvent,
type: RelationMetadataType.MANY_TO_ONE,
label: 'Event ID',
description: 'Event ID',
icon: 'IconCalendar',
inverseSideTarget: () => CalendarEventWorkspaceEntity,
inverseSideFieldKey: 'calendarChannelEventAssociations',
})
calendarEvent: Relation<CalendarEventWorkspaceEntity>;
@WorkspaceJoinColumn('calendarEvent')
calendarEventId: string;
}

View File

@ -0,0 +1,301 @@
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
import {
RelationMetadataType,
RelationOnDeleteAction,
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { CALENDAR_CHANNEL_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator';
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
export enum CalendarChannelVisibility {
METADATA = 'METADATA',
SHARE_EVERYTHING = 'SHARE_EVERYTHING',
}
export enum CalendarChannelSyncStatus {
NOT_SYNCED = 'NOT_SYNCED',
ONGOING = 'ONGOING',
ACTIVE = 'ACTIVE',
FAILED_INSUFFICIENT_PERMISSIONS = 'FAILED_INSUFFICIENT_PERMISSIONS',
FAILED_UNKNOWN = 'FAILED_UNKNOWN',
}
export enum CalendarChannelSyncStage {
FULL_CALENDAR_EVENT_LIST_FETCH_PENDING = 'FULL_CALENDAR_EVENT_LIST_FETCH_PENDING',
PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING = 'PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING',
CALENDAR_EVENT_LIST_FETCH_ONGOING = 'CALENDAR_EVENT_LIST_FETCH_ONGOING',
CALENDAR_EVENTS_IMPORT_PENDING = 'CALENDAR_EVENTS_IMPORT_PENDING',
CALENDAR_EVENTS_IMPORT_ONGOING = 'CALENDAR_EVENTS_IMPORT_ONGOING',
FAILED = 'FAILED',
}
export enum CalendarChannelContactAutoCreationPolicy {
AS_PARTICIPANT_AND_ORGANIZER = 'AS_PARTICIPANT_AND_ORGANIZER',
AS_PARTICIPANT = 'AS_PARTICIPANT',
AS_ORGANIZER = 'AS_ORGANIZER',
NONE = 'NONE',
}
@WorkspaceEntity({
standardId: STANDARD_OBJECT_IDS.calendarChannel,
namePlural: 'calendarChannels',
labelSingular: 'Calendar Channel',
labelPlural: 'Calendar Channels',
description: 'Calendar Channels',
icon: 'IconCalendar',
})
@WorkspaceIsSystem()
@WorkspaceIsNotAuditLogged()
export class CalendarChannelWorkspaceEntity extends BaseWorkspaceEntity {
@WorkspaceField({
standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.handle,
type: FieldMetadataType.TEXT,
label: 'Handle',
description: 'Handle',
icon: 'IconAt',
})
handle: string;
@WorkspaceField({
standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.syncStatus,
type: FieldMetadataType.SELECT,
label: 'Sync status',
description: 'Sync status',
icon: 'IconStatusChange',
options: [
{
value: CalendarChannelSyncStatus.ONGOING,
label: 'Ongoing',
position: 1,
color: 'yellow',
},
{
value: CalendarChannelSyncStatus.NOT_SYNCED,
label: 'Not Synced',
position: 2,
color: 'blue',
},
{
value: CalendarChannelSyncStatus.ACTIVE,
label: 'Active',
position: 3,
color: 'green',
},
{
value: CalendarChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS,
label: 'Failed Insufficient Permissions',
position: 4,
color: 'red',
},
{
value: CalendarChannelSyncStatus.FAILED_UNKNOWN,
label: 'Failed Unknown',
position: 5,
color: 'red',
},
],
})
@WorkspaceIsNullable()
syncStatus: CalendarChannelSyncStatus | null;
@WorkspaceField({
standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.syncStage,
type: FieldMetadataType.SELECT,
label: 'Sync stage',
description: 'Sync stage',
icon: 'IconStatusChange',
options: [
{
value: CalendarChannelSyncStage.FULL_CALENDAR_EVENT_LIST_FETCH_PENDING,
label: 'Full calendar event list fetch pending',
position: 0,
color: 'blue',
},
{
value:
CalendarChannelSyncStage.PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING,
label: 'Partial calendar event list fetch pending',
position: 1,
color: 'blue',
},
{
value: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_ONGOING,
label: 'Calendar event list fetch ongoing',
position: 2,
color: 'orange',
},
{
value: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_PENDING,
label: 'Calendar events import pending',
position: 3,
color: 'blue',
},
{
value: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING,
label: 'Calendar events import ongoing',
position: 4,
color: 'orange',
},
{
value: CalendarChannelSyncStage.FAILED,
label: 'Failed',
position: 5,
color: 'red',
},
],
defaultValue: `'${CalendarChannelSyncStage.FULL_CALENDAR_EVENT_LIST_FETCH_PENDING}'`,
})
syncStage: CalendarChannelSyncStage;
@WorkspaceField({
standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.visibility,
type: FieldMetadataType.SELECT,
label: 'Visibility',
description: 'Visibility',
icon: 'IconEyeglass',
options: [
{
value: CalendarChannelVisibility.METADATA,
label: 'Metadata',
position: 0,
color: 'green',
},
{
value: CalendarChannelVisibility.SHARE_EVERYTHING,
label: 'Share Everything',
position: 1,
color: 'orange',
},
],
defaultValue: `'${CalendarChannelVisibility.SHARE_EVERYTHING}'`,
})
visibility: string;
@WorkspaceField({
standardId:
CALENDAR_CHANNEL_STANDARD_FIELD_IDS.isContactAutoCreationEnabled,
type: FieldMetadataType.BOOLEAN,
label: 'Is Contact Auto Creation Enabled',
description: 'Is Contact Auto Creation Enabled',
icon: 'IconUserCircle',
defaultValue: true,
})
isContactAutoCreationEnabled: boolean;
@WorkspaceField({
standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.contactAutoCreationPolicy,
type: FieldMetadataType.SELECT,
label: 'Contact auto creation policy',
description:
'Automatically create records for people you participated with in an event.',
icon: 'IconUserCircle',
options: [
{
value:
CalendarChannelContactAutoCreationPolicy.AS_PARTICIPANT_AND_ORGANIZER,
label: 'As Participant and Organizer',
color: 'green',
position: 0,
},
{
value: CalendarChannelContactAutoCreationPolicy.AS_PARTICIPANT,
label: 'As Participant',
color: 'orange',
position: 1,
},
{
value: CalendarChannelContactAutoCreationPolicy.AS_ORGANIZER,
label: 'As Organizer',
color: 'blue',
position: 2,
},
{
value: CalendarChannelContactAutoCreationPolicy.NONE,
label: 'None',
color: 'red',
position: 3,
},
],
defaultValue: `'${CalendarChannelContactAutoCreationPolicy.AS_PARTICIPANT_AND_ORGANIZER}'`,
})
contactAutoCreationPolicy: CalendarChannelContactAutoCreationPolicy;
@WorkspaceField({
standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.isSyncEnabled,
type: FieldMetadataType.BOOLEAN,
label: 'Is Sync Enabled',
description: 'Is Sync Enabled',
icon: 'IconRefresh',
defaultValue: true,
})
isSyncEnabled: boolean;
@WorkspaceField({
standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.syncCursor,
type: FieldMetadataType.TEXT,
label: 'Sync Cursor',
description:
'Sync Cursor. Used for syncing events from the calendar provider',
icon: 'IconReload',
})
syncCursor: string;
@WorkspaceField({
standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.syncStageStartedAt,
type: FieldMetadataType.DATE_TIME,
label: 'Sync stage started at',
description: 'Sync stage started at',
icon: 'IconHistory',
})
@WorkspaceIsNullable()
syncStageStartedAt: string | null;
@WorkspaceField({
standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.throttleFailureCount,
type: FieldMetadataType.NUMBER,
label: 'Throttle Failure Count',
description: 'Throttle Failure Count',
icon: 'IconX',
defaultValue: 0,
})
throttleFailureCount: number;
@WorkspaceRelation({
standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.connectedAccount,
type: RelationMetadataType.MANY_TO_ONE,
label: 'Connected Account',
description: 'Connected Account',
icon: 'IconUserCircle',
inverseSideTarget: () => ConnectedAccountWorkspaceEntity,
inverseSideFieldKey: 'calendarChannels',
})
connectedAccount: Relation<ConnectedAccountWorkspaceEntity>;
@WorkspaceJoinColumn('connectedAccount')
connectedAccountId: string;
@WorkspaceRelation({
standardId:
CALENDAR_CHANNEL_STANDARD_FIELD_IDS.calendarChannelEventAssociations,
type: RelationMetadataType.ONE_TO_MANY,
label: 'Calendar Channel Event Associations',
description: 'Calendar Channel Event Associations',
icon: 'IconCalendar',
inverseSideTarget: () => CalendarChannelEventAssociationWorkspaceEntity,
onDelete: RelationOnDeleteAction.CASCADE,
})
calendarChannelEventAssociations: Relation<
CalendarChannelEventAssociationWorkspaceEntity[]
>;
}

View File

@ -0,0 +1,144 @@
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator';
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
export enum CalendarEventParticipantResponseStatus {
NEEDS_ACTION = 'NEEDS_ACTION',
DECLINED = 'DECLINED',
TENTATIVE = 'TENTATIVE',
ACCEPTED = 'ACCEPTED',
}
@WorkspaceEntity({
standardId: STANDARD_OBJECT_IDS.calendarEventParticipant,
namePlural: 'calendarEventParticipants',
labelSingular: 'Calendar event participant',
labelPlural: 'Calendar event participants',
description: 'Calendar event participants',
icon: 'IconCalendar',
})
@WorkspaceIsSystem()
@WorkspaceIsNotAuditLogged()
export class CalendarEventParticipantWorkspaceEntity extends BaseWorkspaceEntity {
@WorkspaceField({
standardId: CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS.handle,
type: FieldMetadataType.TEXT,
label: 'Handle',
description: 'Handle',
icon: 'IconMail',
})
handle: string;
@WorkspaceField({
standardId: CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS.displayName,
type: FieldMetadataType.TEXT,
label: 'Display Name',
description: 'Display Name',
icon: 'IconUser',
})
displayName: string;
@WorkspaceField({
standardId: CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS.isOrganizer,
type: FieldMetadataType.BOOLEAN,
label: 'Is Organizer',
description: 'Is Organizer',
icon: 'IconUser',
defaultValue: false,
})
isOrganizer: boolean;
@WorkspaceField({
standardId: CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS.responseStatus,
type: FieldMetadataType.SELECT,
label: 'Response Status',
description: 'Response Status',
icon: 'IconUser',
options: [
{
value: CalendarEventParticipantResponseStatus.NEEDS_ACTION,
label: 'Needs Action',
position: 0,
color: 'orange',
},
{
value: CalendarEventParticipantResponseStatus.DECLINED,
label: 'Declined',
position: 1,
color: 'red',
},
{
value: CalendarEventParticipantResponseStatus.TENTATIVE,
label: 'Tentative',
position: 2,
color: 'yellow',
},
{
value: CalendarEventParticipantResponseStatus.ACCEPTED,
label: 'Accepted',
position: 3,
color: 'green',
},
],
defaultValue: `'${CalendarEventParticipantResponseStatus.NEEDS_ACTION}'`,
})
responseStatus: string;
@WorkspaceRelation({
standardId: CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS.calendarEvent,
type: RelationMetadataType.MANY_TO_ONE,
label: 'Event ID',
description: 'Event ID',
icon: 'IconCalendar',
inverseSideTarget: () => CalendarEventWorkspaceEntity,
inverseSideFieldKey: 'calendarEventParticipants',
})
calendarEvent: Relation<CalendarEventWorkspaceEntity>;
@WorkspaceJoinColumn('calendarEvent')
calendarEventId: string;
@WorkspaceRelation({
standardId: CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS.person,
type: RelationMetadataType.MANY_TO_ONE,
label: 'Person',
description: 'Person',
icon: 'IconUser',
inverseSideTarget: () => PersonWorkspaceEntity,
inverseSideFieldKey: 'calendarEventParticipants',
})
@WorkspaceIsNullable()
person: Relation<PersonWorkspaceEntity> | null;
@WorkspaceJoinColumn('person')
personId: string | null;
@WorkspaceRelation({
standardId: CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS.workspaceMember,
type: RelationMetadataType.MANY_TO_ONE,
label: 'Workspace Member',
description: 'Workspace Member',
icon: 'IconUser',
inverseSideTarget: () => WorkspaceMemberWorkspaceEntity,
inverseSideFieldKey: 'calendarEventParticipants',
})
@WorkspaceIsNullable()
workspaceMember: Relation<WorkspaceMemberWorkspaceEntity> | null;
@WorkspaceJoinColumn('workspaceMember')
workspaceMemberId: string | null;
}

View File

@ -0,0 +1,180 @@
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import {
RelationMetadataType,
RelationOnDeleteAction,
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
import { CALENDAR_EVENT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { LinkMetadata } from 'src/engine/metadata-modules/field-metadata/composite-types/link.composite-type';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator';
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
@WorkspaceEntity({
standardId: STANDARD_OBJECT_IDS.calendarEvent,
namePlural: 'calendarEvents',
labelSingular: 'Calendar event',
labelPlural: 'Calendar events',
description: 'Calendar events',
icon: 'IconCalendar',
})
@WorkspaceIsSystem()
@WorkspaceIsNotAuditLogged()
export class CalendarEventWorkspaceEntity extends BaseWorkspaceEntity {
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.title,
type: FieldMetadataType.TEXT,
label: 'Title',
description: 'Title',
icon: 'IconH1',
})
title: string;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.isCanceled,
type: FieldMetadataType.BOOLEAN,
label: 'Is canceled',
description: 'Is canceled',
icon: 'IconCalendarCancel',
})
isCanceled: boolean;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.isFullDay,
type: FieldMetadataType.BOOLEAN,
label: 'Is Full Day',
description: 'Is Full Day',
icon: 'Icon24Hours',
})
isFullDay: boolean;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.startsAt,
type: FieldMetadataType.DATE_TIME,
label: 'Start Date',
description: 'Start Date',
icon: 'IconCalendarClock',
})
@WorkspaceIsNullable()
startsAt: string | null;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.endsAt,
type: FieldMetadataType.DATE_TIME,
label: 'End Date',
description: 'End Date',
icon: 'IconCalendarClock',
})
@WorkspaceIsNullable()
endsAt: string | null;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.externalCreatedAt,
type: FieldMetadataType.DATE_TIME,
label: 'Creation DateTime',
description: 'Creation DateTime',
icon: 'IconCalendarPlus',
})
@WorkspaceIsNullable()
externalCreatedAt: string | null;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.externalUpdatedAt,
type: FieldMetadataType.DATE_TIME,
label: 'Update DateTime',
description: 'Update DateTime',
icon: 'IconCalendarCog',
})
@WorkspaceIsNullable()
externalUpdatedAt: string | null;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.description,
type: FieldMetadataType.TEXT,
label: 'Description',
description: 'Description',
icon: 'IconFileDescription',
})
description: string;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.location,
type: FieldMetadataType.TEXT,
label: 'Location',
description: 'Location',
icon: 'IconMapPin',
})
location: string;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.iCalUID,
type: FieldMetadataType.TEXT,
label: 'iCal UID',
description: 'iCal UID',
icon: 'IconKey',
})
iCalUID: string;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.conferenceSolution,
type: FieldMetadataType.TEXT,
label: 'Conference Solution',
description: 'Conference Solution',
icon: 'IconScreenShare',
})
conferenceSolution: string;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.conferenceLink,
type: FieldMetadataType.LINK,
label: 'Meet Link',
description: 'Meet Link',
icon: 'IconLink',
})
@WorkspaceIsNullable()
conferenceLink: LinkMetadata;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.recurringEventExternalId,
type: FieldMetadataType.TEXT,
label: 'Recurring Event ID',
description: 'Recurring Event ID',
icon: 'IconHistory',
})
recurringEventExternalId: string;
@WorkspaceRelation({
standardId:
CALENDAR_EVENT_STANDARD_FIELD_IDS.calendarChannelEventAssociations,
type: RelationMetadataType.ONE_TO_MANY,
label: 'Calendar Channel Event Associations',
description: 'Calendar Channel Event Associations',
icon: 'IconCalendar',
inverseSideTarget: () => CalendarChannelEventAssociationWorkspaceEntity,
onDelete: RelationOnDeleteAction.CASCADE,
})
calendarChannelEventAssociations: Relation<
CalendarChannelEventAssociationWorkspaceEntity[]
>;
@WorkspaceRelation({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.calendarEventParticipants,
type: RelationMetadataType.ONE_TO_MANY,
label: 'Event Participants',
description: 'Event Participants',
icon: 'IconUserCircle',
inverseSideTarget: () => CalendarEventParticipantWorkspaceEntity,
onDelete: RelationOnDeleteAction.CASCADE,
})
calendarEventParticipants: Relation<
CalendarEventParticipantWorkspaceEntity[]
>;
}

View File

@ -0,0 +1,37 @@
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
export type CalendarEvent = Omit<
CalendarEventWorkspaceEntity,
| 'createdAt'
| 'updatedAt'
| 'calendarChannelEventAssociations'
| 'calendarEventParticipants'
| 'conferenceLink'
> & {
conferenceLinkLabel: string;
conferenceLinkUrl: string;
};
export type CalendarEventParticipant = Omit<
CalendarEventParticipantWorkspaceEntity,
| 'id'
| 'createdAt'
| 'updatedAt'
| 'personId'
| 'workspaceMemberId'
| 'person'
| 'workspaceMember'
| 'calendarEvent'
> & {
iCalUID: string;
};
export type CalendarEventWithParticipants = CalendarEvent & {
externalId: string;
participants: CalendarEventParticipant[];
};
export type CalendarEventParticipantWithId = CalendarEventParticipant & {
id: string;
};