mirror of
https://github.com/lingble/twenty.git
synced 2025-11-01 21:27:58 +00:00
4485 create a custom resolver for calendar events (#4568)
* create timeline calendar event resolver * working on getCalendarEventsFromPersonIds * add count query * add calendarEventVisibility and add typing * update calendarEvent dto * modify calendarEvent dto * compute calendar event visibility * fix types * add FieldMetadata in timeline calendar dtos and create queries and fragments * remove fieldMatadata * fix naming * update resolver * add getCalendarEventsFromCompanyId * fix queries * refactor queries * fix visibility * fix calendar event attendees bug * visibility is working * remove @IDField * update gql queries * update dto * add error * add enum * throw http exception * modify error * Refactor calendar event visibility check * use enum
This commit is contained in:
@@ -61,7 +61,7 @@ export type AuthTokens = {
|
||||
export type Billing = {
|
||||
__typename?: 'Billing';
|
||||
billingFreeTrialDurationInDays?: Maybe<Scalars['Float']>;
|
||||
billingUrl: Scalars['String'];
|
||||
billingUrl?: Maybe<Scalars['String']>;
|
||||
isBillingEnabled: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
@@ -417,6 +417,8 @@ export type Query = {
|
||||
currentWorkspace: Workspace;
|
||||
findWorkspaceFromInviteHash: Workspace;
|
||||
getProductPrices: ProductPricesEntity;
|
||||
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
|
||||
getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal;
|
||||
getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal;
|
||||
getTimelineThreadsFromPersonId: TimelineThreadsWithTotal;
|
||||
object: Object;
|
||||
@@ -450,6 +452,20 @@ export type QueryGetProductPricesArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetTimelineCalendarEventsFromCompanyIdArgs = {
|
||||
companyId: Scalars['ID'];
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetTimelineCalendarEventsFromPersonIdArgs = {
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
personId: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetTimelineThreadsFromCompanyIdArgs = {
|
||||
companyId: Scalars['ID'];
|
||||
page: Scalars['Int'];
|
||||
@@ -492,6 +508,23 @@ export type RelationConnection = {
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
export type RelationDefinition = {
|
||||
__typename?: 'RelationDefinition';
|
||||
direction: RelationDefinitionType;
|
||||
sourceFieldMetadata: Field;
|
||||
sourceObjectMetadata: Object;
|
||||
targetFieldMetadata: Field;
|
||||
targetObjectMetadata: Object;
|
||||
};
|
||||
|
||||
/** Relation definition type */
|
||||
export enum RelationDefinitionType {
|
||||
ManyToMany = 'MANY_TO_MANY',
|
||||
ManyToOne = 'MANY_TO_ONE',
|
||||
OneToMany = 'ONE_TO_MANY',
|
||||
OneToOne = 'ONE_TO_ONE'
|
||||
}
|
||||
|
||||
export type RelationDeleteResponse = {
|
||||
__typename?: 'RelationDeleteResponse';
|
||||
createdAt?: Maybe<Scalars['DateTime']>;
|
||||
@@ -545,6 +578,45 @@ export type Telemetry = {
|
||||
enabled: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type TimelineCalendarEvent = {
|
||||
__typename?: 'TimelineCalendarEvent';
|
||||
attendees: Array<TimelineCalendarEventAttendee>;
|
||||
conferenceSolution: Scalars['String'];
|
||||
conferenceUri: Scalars['String'];
|
||||
description: Scalars['String'];
|
||||
endsAt: Scalars['DateTime'];
|
||||
id: Scalars['ID'];
|
||||
isCanceled: Scalars['Boolean'];
|
||||
isFullDay: Scalars['Boolean'];
|
||||
location: Scalars['String'];
|
||||
startsAt: Scalars['DateTime'];
|
||||
title: Scalars['String'];
|
||||
visibility: TimelineCalendarEventVisibility;
|
||||
};
|
||||
|
||||
export type TimelineCalendarEventAttendee = {
|
||||
__typename?: 'TimelineCalendarEventAttendee';
|
||||
avatarUrl: Scalars['String'];
|
||||
displayName: Scalars['String'];
|
||||
firstName: Scalars['String'];
|
||||
handle: Scalars['String'];
|
||||
lastName: Scalars['String'];
|
||||
personId?: Maybe<Scalars['ID']>;
|
||||
workspaceMemberId?: Maybe<Scalars['ID']>;
|
||||
};
|
||||
|
||||
/** Visibility of the calendar event */
|
||||
export enum TimelineCalendarEventVisibility {
|
||||
Metadata = 'METADATA',
|
||||
ShareEverything = 'SHARE_EVERYTHING'
|
||||
}
|
||||
|
||||
export type TimelineCalendarEventsWithTotal = {
|
||||
__typename?: 'TimelineCalendarEventsWithTotal';
|
||||
timelineCalendarEvents: Array<TimelineCalendarEvent>;
|
||||
totalNumberOfCalendarEvents: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type TimelineThread = {
|
||||
__typename?: 'TimelineThread';
|
||||
firstParticipant: TimelineThreadParticipant;
|
||||
@@ -716,6 +788,7 @@ export type Field = {
|
||||
label: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
options?: Maybe<Scalars['JSON']>;
|
||||
relationDefinition?: Maybe<RelationDefinition>;
|
||||
toRelationMetadata?: Maybe<Relation>;
|
||||
type: FieldMetadataType;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
@@ -794,6 +867,30 @@ export type RelationEdge = {
|
||||
node: Relation;
|
||||
};
|
||||
|
||||
export type AttendeeFragmentFragment = { __typename?: 'TimelineCalendarEventAttendee', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string };
|
||||
|
||||
export type CalendarEventFragmentFragment = { __typename?: 'TimelineCalendarEvent', id: string, title: string, description: string, location: string, startsAt: string, endsAt: string, isFullDay: boolean, attendees: Array<{ __typename?: 'TimelineCalendarEventAttendee', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> };
|
||||
|
||||
export type TimelineCalendarEventsWithTotalFragmentFragment = { __typename?: 'TimelineCalendarEventsWithTotal', totalNumberOfCalendarEvents: number, timelineCalendarEvents: Array<{ __typename?: 'TimelineCalendarEvent', id: string, title: string, description: string, location: string, startsAt: string, endsAt: string, isFullDay: boolean, attendees: Array<{ __typename?: 'TimelineCalendarEventAttendee', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> };
|
||||
|
||||
export type GetTimelineCalendarEventsFromCompanyIdQueryVariables = Exact<{
|
||||
companyId: Scalars['ID'];
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetTimelineCalendarEventsFromCompanyIdQuery = { __typename?: 'Query', getTimelineCalendarEventsFromCompanyId: { __typename?: 'TimelineCalendarEventsWithTotal', totalNumberOfCalendarEvents: number, timelineCalendarEvents: Array<{ __typename?: 'TimelineCalendarEvent', id: string, title: string, description: string, location: string, startsAt: string, endsAt: string, isFullDay: boolean, attendees: Array<{ __typename?: 'TimelineCalendarEventAttendee', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } };
|
||||
|
||||
export type GetTimelineCalendarEventsFromPersonIdQueryVariables = Exact<{
|
||||
personId: Scalars['ID'];
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetTimelineCalendarEventsFromPersonIdQuery = { __typename?: 'Query', getTimelineCalendarEventsFromPersonId: { __typename?: 'TimelineCalendarEventsWithTotal', totalNumberOfCalendarEvents: number, timelineCalendarEvents: Array<{ __typename?: 'TimelineCalendarEvent', id: string, title: string, description: string, location: string, startsAt: string, endsAt: string, isFullDay: boolean, attendees: Array<{ __typename?: 'TimelineCalendarEventAttendee', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } };
|
||||
|
||||
export type ParticipantFragmentFragment = { __typename?: 'TimelineThreadParticipant', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string };
|
||||
|
||||
export type TimelineThreadFragmentFragment = { __typename?: 'TimelineThread', id: string, read: boolean, visibility: string, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> };
|
||||
@@ -937,7 +1034,7 @@ export type GetProductPricesQuery = { __typename?: 'Query', getProductPrices: {
|
||||
export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, signUpDisabled: boolean, debugMode: boolean, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean }, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl: string, billingFreeTrialDurationInDays?: number | null }, telemetry: { __typename?: 'Telemetry', enabled: boolean, anonymizationEnabled: boolean }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null } } };
|
||||
export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, signUpDisabled: boolean, debugMode: boolean, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean }, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, billingFreeTrialDurationInDays?: number | null }, telemetry: { __typename?: 'Telemetry', enabled: boolean, anonymizationEnabled: boolean }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null } } };
|
||||
|
||||
export type UploadFileMutationVariables = Exact<{
|
||||
file: Scalars['Upload'];
|
||||
@@ -1007,6 +1104,39 @@ export type GetWorkspaceFromInviteHashQueryVariables = Exact<{
|
||||
|
||||
export type GetWorkspaceFromInviteHashQuery = { __typename?: 'Query', findWorkspaceFromInviteHash: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, allowImpersonation: boolean } };
|
||||
|
||||
export const AttendeeFragmentFragmentDoc = gql`
|
||||
fragment AttendeeFragment on TimelineCalendarEventAttendee {
|
||||
personId
|
||||
workspaceMemberId
|
||||
firstName
|
||||
lastName
|
||||
displayName
|
||||
avatarUrl
|
||||
handle
|
||||
}
|
||||
`;
|
||||
export const CalendarEventFragmentFragmentDoc = gql`
|
||||
fragment CalendarEventFragment on TimelineCalendarEvent {
|
||||
id
|
||||
title
|
||||
description
|
||||
location
|
||||
startsAt
|
||||
endsAt
|
||||
isFullDay
|
||||
attendees {
|
||||
...AttendeeFragment
|
||||
}
|
||||
}
|
||||
${AttendeeFragmentFragmentDoc}`;
|
||||
export const TimelineCalendarEventsWithTotalFragmentFragmentDoc = gql`
|
||||
fragment TimelineCalendarEventsWithTotalFragment on TimelineCalendarEventsWithTotal {
|
||||
totalNumberOfCalendarEvents
|
||||
timelineCalendarEvents {
|
||||
...CalendarEventFragment
|
||||
}
|
||||
}
|
||||
${CalendarEventFragmentFragmentDoc}`;
|
||||
export const ParticipantFragmentFragmentDoc = gql`
|
||||
fragment ParticipantFragment on TimelineThreadParticipant {
|
||||
personId
|
||||
@@ -1103,6 +1233,88 @@ export const UserQueryFragmentFragmentDoc = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const GetTimelineCalendarEventsFromCompanyIdDocument = gql`
|
||||
query GetTimelineCalendarEventsFromCompanyId($companyId: ID!, $page: Int!, $pageSize: Int!) {
|
||||
getTimelineCalendarEventsFromCompanyId(
|
||||
companyId: $companyId
|
||||
page: $page
|
||||
pageSize: $pageSize
|
||||
) {
|
||||
...TimelineCalendarEventsWithTotalFragment
|
||||
}
|
||||
}
|
||||
${TimelineCalendarEventsWithTotalFragmentFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useGetTimelineCalendarEventsFromCompanyIdQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetTimelineCalendarEventsFromCompanyIdQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetTimelineCalendarEventsFromCompanyIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useGetTimelineCalendarEventsFromCompanyIdQuery({
|
||||
* variables: {
|
||||
* companyId: // value for 'companyId'
|
||||
* page: // value for 'page'
|
||||
* pageSize: // value for 'pageSize'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetTimelineCalendarEventsFromCompanyIdQuery(baseOptions: Apollo.QueryHookOptions<GetTimelineCalendarEventsFromCompanyIdQuery, GetTimelineCalendarEventsFromCompanyIdQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<GetTimelineCalendarEventsFromCompanyIdQuery, GetTimelineCalendarEventsFromCompanyIdQueryVariables>(GetTimelineCalendarEventsFromCompanyIdDocument, options);
|
||||
}
|
||||
export function useGetTimelineCalendarEventsFromCompanyIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetTimelineCalendarEventsFromCompanyIdQuery, GetTimelineCalendarEventsFromCompanyIdQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<GetTimelineCalendarEventsFromCompanyIdQuery, GetTimelineCalendarEventsFromCompanyIdQueryVariables>(GetTimelineCalendarEventsFromCompanyIdDocument, options);
|
||||
}
|
||||
export type GetTimelineCalendarEventsFromCompanyIdQueryHookResult = ReturnType<typeof useGetTimelineCalendarEventsFromCompanyIdQuery>;
|
||||
export type GetTimelineCalendarEventsFromCompanyIdLazyQueryHookResult = ReturnType<typeof useGetTimelineCalendarEventsFromCompanyIdLazyQuery>;
|
||||
export type GetTimelineCalendarEventsFromCompanyIdQueryResult = Apollo.QueryResult<GetTimelineCalendarEventsFromCompanyIdQuery, GetTimelineCalendarEventsFromCompanyIdQueryVariables>;
|
||||
export const GetTimelineCalendarEventsFromPersonIdDocument = gql`
|
||||
query GetTimelineCalendarEventsFromPersonId($personId: ID!, $page: Int!, $pageSize: Int!) {
|
||||
getTimelineCalendarEventsFromPersonId(
|
||||
personId: $personId
|
||||
page: $page
|
||||
pageSize: $pageSize
|
||||
) {
|
||||
...TimelineCalendarEventsWithTotalFragment
|
||||
}
|
||||
}
|
||||
${TimelineCalendarEventsWithTotalFragmentFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useGetTimelineCalendarEventsFromPersonIdQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetTimelineCalendarEventsFromPersonIdQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetTimelineCalendarEventsFromPersonIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useGetTimelineCalendarEventsFromPersonIdQuery({
|
||||
* variables: {
|
||||
* personId: // value for 'personId'
|
||||
* page: // value for 'page'
|
||||
* pageSize: // value for 'pageSize'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetTimelineCalendarEventsFromPersonIdQuery(baseOptions: Apollo.QueryHookOptions<GetTimelineCalendarEventsFromPersonIdQuery, GetTimelineCalendarEventsFromPersonIdQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<GetTimelineCalendarEventsFromPersonIdQuery, GetTimelineCalendarEventsFromPersonIdQueryVariables>(GetTimelineCalendarEventsFromPersonIdDocument, options);
|
||||
}
|
||||
export function useGetTimelineCalendarEventsFromPersonIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetTimelineCalendarEventsFromPersonIdQuery, GetTimelineCalendarEventsFromPersonIdQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<GetTimelineCalendarEventsFromPersonIdQuery, GetTimelineCalendarEventsFromPersonIdQueryVariables>(GetTimelineCalendarEventsFromPersonIdDocument, options);
|
||||
}
|
||||
export type GetTimelineCalendarEventsFromPersonIdQueryHookResult = ReturnType<typeof useGetTimelineCalendarEventsFromPersonIdQuery>;
|
||||
export type GetTimelineCalendarEventsFromPersonIdLazyQueryHookResult = ReturnType<typeof useGetTimelineCalendarEventsFromPersonIdLazyQuery>;
|
||||
export type GetTimelineCalendarEventsFromPersonIdQueryResult = Apollo.QueryResult<GetTimelineCalendarEventsFromPersonIdQuery, GetTimelineCalendarEventsFromPersonIdQueryVariables>;
|
||||
export const GetTimelineThreadsFromCompanyIdDocument = gql`
|
||||
query GetTimelineThreadsFromCompanyId($companyId: ID!, $page: Int!, $pageSize: Int!) {
|
||||
getTimelineThreadsFromCompanyId(
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const attendeeFragment = gql`
|
||||
fragment AttendeeFragment on TimelineCalendarEventAttendee {
|
||||
personId
|
||||
workspaceMemberId
|
||||
firstName
|
||||
lastName
|
||||
displayName
|
||||
avatarUrl
|
||||
handle
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,19 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { attendeeFragment } from '@/activities/calendar/queries/fragments/attendeeFragment';
|
||||
|
||||
export const calendarEventFragment = gql`
|
||||
fragment CalendarEventFragment on TimelineCalendarEvent {
|
||||
id
|
||||
title
|
||||
description
|
||||
location
|
||||
startsAt
|
||||
endsAt
|
||||
isFullDay
|
||||
attendees {
|
||||
...AttendeeFragment
|
||||
}
|
||||
}
|
||||
${attendeeFragment}
|
||||
`;
|
||||
@@ -0,0 +1,13 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { calendarEventFragment } from '@/activities/calendar/queries/fragments/calendarEventFragment';
|
||||
|
||||
export const timelineCalendarEventWithTotalFragment = gql`
|
||||
fragment TimelineCalendarEventsWithTotalFragment on TimelineCalendarEventsWithTotal {
|
||||
totalNumberOfCalendarEvents
|
||||
timelineCalendarEvents {
|
||||
...CalendarEventFragment
|
||||
}
|
||||
}
|
||||
${calendarEventFragment}
|
||||
`;
|
||||
@@ -0,0 +1,20 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineCalendarEventWithTotalFragment } from '@/activities/calendar/queries/fragments/calendarEventFragmentWithTotalFragment';
|
||||
|
||||
export const getTimelineCalendarEventsFromCompanyId = gql`
|
||||
query GetTimelineCalendarEventsFromCompanyId(
|
||||
$companyId: ID!
|
||||
$page: Int!
|
||||
$pageSize: Int!
|
||||
) {
|
||||
getTimelineCalendarEventsFromCompanyId(
|
||||
companyId: $companyId
|
||||
page: $page
|
||||
pageSize: $pageSize
|
||||
) {
|
||||
...TimelineCalendarEventsWithTotalFragment
|
||||
}
|
||||
}
|
||||
${timelineCalendarEventWithTotalFragment}
|
||||
`;
|
||||
@@ -0,0 +1,20 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineCalendarEventWithTotalFragment } from '@/activities/calendar/queries/fragments/calendarEventFragmentWithTotalFragment';
|
||||
|
||||
export const getTimelineCalendarEventsFromPersonId = gql`
|
||||
query GetTimelineCalendarEventsFromPersonId(
|
||||
$personId: ID!
|
||||
$page: Int!
|
||||
$pageSize: Int!
|
||||
) {
|
||||
getTimelineCalendarEventsFromPersonId(
|
||||
personId: $personId
|
||||
page: $page
|
||||
pageSize: $pageSize
|
||||
) {
|
||||
...TimelineCalendarEventsWithTotalFragment
|
||||
}
|
||||
}
|
||||
${timelineCalendarEventWithTotalFragment}
|
||||
`;
|
||||
@@ -0,0 +1,2 @@
|
||||
export const TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE = 20;
|
||||
export const TIMELINE_CALENDAR_EVENTS_MAX_PAGE_SIZE = 50;
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ObjectType, Field, ID } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType('TimelineCalendarEventAttendee')
|
||||
export class TimelineCalendarEventAttendee {
|
||||
@Field(() => ID, { nullable: true })
|
||||
personId: string;
|
||||
|
||||
@Field(() => ID, { nullable: true })
|
||||
workspaceMemberId: string;
|
||||
|
||||
@Field()
|
||||
firstName: string;
|
||||
|
||||
@Field()
|
||||
lastName: string;
|
||||
|
||||
@Field()
|
||||
displayName: string;
|
||||
|
||||
@Field()
|
||||
avatarUrl: string;
|
||||
|
||||
@Field()
|
||||
handle: string;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { ObjectType, ID, Field, registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
import { TimelineCalendarEventAttendee } from 'src/engine/modules/calendar/dtos/timeline-calendar-event-attendee.dto';
|
||||
|
||||
export enum TimelineCalendarEventVisibility {
|
||||
METADATA = 'METADATA',
|
||||
SHARE_EVERYTHING = 'SHARE_EVERYTHING',
|
||||
}
|
||||
|
||||
registerEnumType(TimelineCalendarEventVisibility, {
|
||||
name: 'TimelineCalendarEventVisibility',
|
||||
description: 'Visibility of the calendar event',
|
||||
});
|
||||
|
||||
@ObjectType('TimelineCalendarEvent')
|
||||
export class TimelineCalendarEvent {
|
||||
@Field(() => ID)
|
||||
id: string;
|
||||
|
||||
@Field()
|
||||
title: string;
|
||||
|
||||
@Field()
|
||||
isCanceled: boolean;
|
||||
|
||||
@Field()
|
||||
isFullDay: boolean;
|
||||
|
||||
@Field()
|
||||
startsAt: Date;
|
||||
|
||||
@Field()
|
||||
endsAt: Date;
|
||||
|
||||
@Field()
|
||||
description: string;
|
||||
|
||||
@Field()
|
||||
location: string;
|
||||
|
||||
@Field()
|
||||
conferenceSolution: string;
|
||||
|
||||
@Field()
|
||||
conferenceUri: string;
|
||||
|
||||
@Field(() => [TimelineCalendarEventAttendee])
|
||||
attendees: TimelineCalendarEventAttendee[];
|
||||
|
||||
@Field(() => TimelineCalendarEventVisibility)
|
||||
visibility: TimelineCalendarEventVisibility;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Field, Int, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { TimelineCalendarEvent } from 'src/engine/modules/calendar/dtos/timeline-calendar-event.dto';
|
||||
|
||||
@ObjectType('TimelineCalendarEventsWithTotal')
|
||||
export class TimelineCalendarEventsWithTotal {
|
||||
@Field(() => Int)
|
||||
totalNumberOfCalendarEvents: number;
|
||||
|
||||
@Field(() => [TimelineCalendarEvent])
|
||||
timelineCalendarEvents: TimelineCalendarEvent[];
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
import { UserModule } from 'src/engine/modules/user/user.module';
|
||||
import { TimelineCalendarEventResolver } from 'src/engine/modules/calendar/timeline-calendar-event.resolver';
|
||||
import { TimelineCalendarEventService } from 'src/engine/modules/calendar/timeline-calendar-event.service';
|
||||
@Module({
|
||||
imports: [WorkspaceDataSourceModule, UserModule],
|
||||
exports: [],
|
||||
providers: [TimelineCalendarEventResolver, TimelineCalendarEventService],
|
||||
})
|
||||
export class TimelineCalendarEventModule {}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
import {
|
||||
Query,
|
||||
Args,
|
||||
ArgsType,
|
||||
Field,
|
||||
ID,
|
||||
Int,
|
||||
Resolver,
|
||||
} from '@nestjs/graphql';
|
||||
|
||||
import { Max } from 'class-validator';
|
||||
|
||||
import { User } from 'src/engine/modules/user/user.entity';
|
||||
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { JwtAuthGuard } from 'src/engine/guards/jwt.auth.guard';
|
||||
import { TIMELINE_CALENDAR_EVENTS_MAX_PAGE_SIZE } from 'src/engine/modules/calendar/constants/calendar.constants';
|
||||
import { TimelineCalendarEventsWithTotal } from 'src/engine/modules/calendar/dtos/timeline-calendar-events-with-total.dto';
|
||||
import { TimelineCalendarEventService } from 'src/engine/modules/calendar/timeline-calendar-event.service';
|
||||
import { UserService } from 'src/engine/modules/user/services/user.service';
|
||||
import { Workspace } from 'src/engine/modules/workspace/workspace.entity';
|
||||
import { NotFoundError } from 'src/engine/filters/utils/graphql-errors.util';
|
||||
|
||||
@ArgsType()
|
||||
class GetTimelineCalendarEventsFromPersonIdArgs {
|
||||
@Field(() => ID)
|
||||
personId: string;
|
||||
|
||||
@Field(() => Int)
|
||||
page: number;
|
||||
|
||||
@Field(() => Int)
|
||||
@Max(TIMELINE_CALENDAR_EVENTS_MAX_PAGE_SIZE)
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
class GetTimelineCalendarEventsFromCompanyIdArgs {
|
||||
@Field(() => ID)
|
||||
companyId: string;
|
||||
|
||||
@Field(() => Int)
|
||||
page: number;
|
||||
|
||||
@Field(() => Int)
|
||||
@Max(TIMELINE_CALENDAR_EVENTS_MAX_PAGE_SIZE)
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => TimelineCalendarEventsWithTotal)
|
||||
export class TimelineCalendarEventResolver {
|
||||
constructor(
|
||||
private readonly timelineCalendarEventService: TimelineCalendarEventService,
|
||||
private readonly userService: UserService,
|
||||
) {}
|
||||
|
||||
@Query(() => TimelineCalendarEventsWithTotal)
|
||||
async getTimelineCalendarEventsFromPersonId(
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
@AuthUser() user: User,
|
||||
@Args()
|
||||
{ personId, page, pageSize }: GetTimelineCalendarEventsFromPersonIdArgs,
|
||||
) {
|
||||
const workspaceMember = await this.userService.loadWorkspaceMember(user);
|
||||
|
||||
if (!workspaceMember) {
|
||||
throw new NotFoundError('Workspace member not found');
|
||||
}
|
||||
|
||||
const timelineCalendarEvents =
|
||||
await this.timelineCalendarEventService.getCalendarEventsFromPersonIds(
|
||||
workspaceMember.id,
|
||||
workspaceId,
|
||||
[personId],
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return timelineCalendarEvents;
|
||||
}
|
||||
|
||||
@Query(() => TimelineCalendarEventsWithTotal)
|
||||
async getTimelineCalendarEventsFromCompanyId(
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
@AuthUser() user: User,
|
||||
@Args()
|
||||
{ companyId, page, pageSize }: GetTimelineCalendarEventsFromCompanyIdArgs,
|
||||
) {
|
||||
const workspaceMember = await this.userService.loadWorkspaceMember(user);
|
||||
|
||||
if (!workspaceMember) {
|
||||
throw new NotFoundError('Workspace member not found');
|
||||
}
|
||||
|
||||
const timelineCalendarEvents =
|
||||
await this.timelineCalendarEventService.getCalendarEventsFromCompanyId(
|
||||
workspaceMember.id,
|
||||
workspaceId,
|
||||
companyId,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return timelineCalendarEvents;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import groupBy from 'lodash.groupBy';
|
||||
|
||||
import { TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE } from 'src/engine/modules/calendar/constants/calendar.constants';
|
||||
import { TimelineCalendarEventAttendee } from 'src/engine/modules/calendar/dtos/timeline-calendar-event-attendee.dto';
|
||||
import {
|
||||
TimelineCalendarEvent,
|
||||
TimelineCalendarEventVisibility,
|
||||
} from 'src/engine/modules/calendar/dtos/timeline-calendar-event.dto';
|
||||
import { TimelineCalendarEventsWithTotal } from 'src/engine/modules/calendar/dtos/timeline-calendar-events-with-total.dto';
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
|
||||
import { CalendarEventAttendeeObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-event-attendee.object-metadata';
|
||||
|
||||
type TimelineCalendarEventAttendeeWithPersonInformation =
|
||||
ObjectRecord<CalendarEventAttendeeObjectMetadata> & {
|
||||
personFirstName: string;
|
||||
personLastName: string;
|
||||
personAvatarUrl: string;
|
||||
workspaceMemberFirstName: string;
|
||||
workspaceMemberLastName: string;
|
||||
workspaceMemberAvatarUrl: string;
|
||||
};
|
||||
@Injectable()
|
||||
export class TimelineCalendarEventService {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
) {}
|
||||
|
||||
async getCalendarEventsFromPersonIds(
|
||||
workspaceMemberId: string,
|
||||
workspaceId: string,
|
||||
personIds: string[],
|
||||
page: number = 1,
|
||||
pageSize: number = TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE,
|
||||
): Promise<TimelineCalendarEventsWithTotal> {
|
||||
const offset = (page - 1) * pageSize;
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const calendarEvents: Omit<TimelineCalendarEvent, 'attendees'>[] =
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT
|
||||
"calendarEvent".*
|
||||
FROM
|
||||
${dataSourceSchema}."calendarEvent" "calendarEvent"
|
||||
LEFT JOIN
|
||||
${dataSourceSchema}."calendarEventAttendee" "calendarEventAttendee" ON "calendarEvent".id = "calendarEventAttendee"."calendarEventId"
|
||||
LEFT JOIN
|
||||
${dataSourceSchema}."person" "person" ON "calendarEventAttendee"."personId" = "person".id
|
||||
WHERE
|
||||
"calendarEventAttendee"."personId" = ANY($1)
|
||||
GROUP BY
|
||||
"calendarEvent".id
|
||||
ORDER BY
|
||||
"calendarEvent"."startsAt" DESC
|
||||
LIMIT $2
|
||||
OFFSET $3`,
|
||||
[personIds, pageSize, offset],
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!calendarEvents) {
|
||||
return {
|
||||
totalNumberOfCalendarEvents: 0,
|
||||
timelineCalendarEvents: [],
|
||||
};
|
||||
}
|
||||
|
||||
const calendarEventAttendees: TimelineCalendarEventAttendeeWithPersonInformation[] =
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT
|
||||
"calendarEventAttendee".*,
|
||||
"person"."nameFirstName" as "personFirstName",
|
||||
"person"."nameLastName" as "personLastName",
|
||||
"person"."avatarUrl" as "personAvatarUrl",
|
||||
"workspaceMember"."nameFirstName" as "workspaceMemberFirstName",
|
||||
"workspaceMember"."nameLastName" as "workspaceMemberLastName",
|
||||
"workspaceMember"."avatarUrl" as "workspaceMemberAvatarUrl"
|
||||
FROM
|
||||
${dataSourceSchema}."calendarEventAttendee" "calendarEventAttendee"
|
||||
LEFT JOIN
|
||||
${dataSourceSchema}."person" "person" ON "calendarEventAttendee"."personId" = "person".id
|
||||
LEFT JOIN
|
||||
${dataSourceSchema}."workspaceMember" "workspaceMember" ON "calendarEventAttendee"."workspaceMemberId" = "workspaceMember".id
|
||||
WHERE
|
||||
"calendarEventAttendee"."calendarEventId" = ANY($1)`,
|
||||
[calendarEvents.map((event) => event.id)],
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const formattedCalendarEventAttendees: TimelineCalendarEventAttendee[] =
|
||||
calendarEventAttendees.map((attendee) => {
|
||||
const firstName =
|
||||
attendee.personFirstName || attendee.workspaceMemberFirstName || '';
|
||||
|
||||
const lastName =
|
||||
attendee.personLastName || attendee.workspaceMemberLastName || '';
|
||||
|
||||
const displayName =
|
||||
firstName || attendee.displayName || attendee.handle;
|
||||
|
||||
const avatarUrl =
|
||||
attendee.personAvatarUrl || attendee.workspaceMemberAvatarUrl || '';
|
||||
|
||||
return {
|
||||
calendarEventId: attendee.calendarEventId,
|
||||
personId: attendee.personId,
|
||||
workspaceMemberId: attendee.workspaceMemberId,
|
||||
firstName,
|
||||
lastName,
|
||||
displayName,
|
||||
avatarUrl,
|
||||
handle: attendee.handle,
|
||||
};
|
||||
});
|
||||
|
||||
const calendarEventAttendeesByEventId: {
|
||||
[calendarEventId: string]: TimelineCalendarEventAttendee[];
|
||||
} = groupBy(formattedCalendarEventAttendees, 'calendarEventId');
|
||||
|
||||
const totalNumberOfCalendarEvents: { count: number }[] =
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`
|
||||
SELECT
|
||||
COUNT(DISTINCT "calendarEventId")
|
||||
FROM
|
||||
${dataSourceSchema}."calendarEventAttendee" "calendarEventAttendee"
|
||||
WHERE
|
||||
"calendarEventAttendee"."personId" = ANY($1)
|
||||
`,
|
||||
[personIds],
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const timelineCalendarEvents = calendarEvents.map((event) => {
|
||||
const attendees = calendarEventAttendeesByEventId[event.id] || [];
|
||||
|
||||
return {
|
||||
...event,
|
||||
attendees,
|
||||
};
|
||||
});
|
||||
|
||||
const calendarEventIdsWithWorkspaceMemberInAttendees =
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`
|
||||
SELECT
|
||||
"calendarEventId"
|
||||
FROM
|
||||
${dataSourceSchema}."calendarEventAttendee" "calendarEventAttendee"
|
||||
WHERE
|
||||
"calendarEventAttendee"."workspaceMemberId" = $1
|
||||
`,
|
||||
[workspaceMemberId],
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const calendarEventIdsWithWorkspaceMemberInAttendeesFormatted =
|
||||
calendarEventIdsWithWorkspaceMemberInAttendees.map(
|
||||
(event: { calendarEventId: string }) => event.calendarEventId,
|
||||
);
|
||||
|
||||
const calendarEventIdsToFetchVisibilityFor = timelineCalendarEvents
|
||||
.filter(
|
||||
(event) =>
|
||||
!calendarEventIdsWithWorkspaceMemberInAttendeesFormatted.includes(
|
||||
event.id,
|
||||
),
|
||||
)
|
||||
.map((event) => event.id);
|
||||
|
||||
const calendarEventIdsForWhichVisibilityIsMetadata:
|
||||
| {
|
||||
id: string;
|
||||
}[]
|
||||
| undefined = await this.workspaceDataSourceService.executeRawQuery(
|
||||
`
|
||||
SELECT
|
||||
"calendarChannelEventAssociation"."calendarEventId" AS "id"
|
||||
FROM
|
||||
${dataSourceSchema}."calendarChannel" "calendarChannel"
|
||||
LEFT JOIN
|
||||
${dataSourceSchema}."calendarChannelEventAssociation" "calendarChannelEventAssociation" ON "calendarChannel".id = "calendarChannelEventAssociation"."calendarChannelId"
|
||||
WHERE
|
||||
"calendarChannelEventAssociation"."calendarEventId" = ANY($1)
|
||||
AND
|
||||
"calendarChannel"."visibility" = 'METADATA'
|
||||
`,
|
||||
[calendarEventIdsToFetchVisibilityFor],
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!calendarEventIdsForWhichVisibilityIsMetadata) {
|
||||
throw new Error('Failed to fetch calendar event visibility');
|
||||
}
|
||||
|
||||
const calendarEventIdsForWhichVisibilityIsMetadataMap = new Map(
|
||||
calendarEventIdsForWhichVisibilityIsMetadata.map((event) => [
|
||||
event.id,
|
||||
TimelineCalendarEventVisibility.METADATA,
|
||||
]),
|
||||
);
|
||||
|
||||
timelineCalendarEvents.forEach((event) => {
|
||||
event.visibility =
|
||||
calendarEventIdsForWhichVisibilityIsMetadataMap.get(event.id) ??
|
||||
TimelineCalendarEventVisibility.SHARE_EVERYTHING;
|
||||
|
||||
if (event.visibility === TimelineCalendarEventVisibility.METADATA) {
|
||||
event.title = '';
|
||||
event.description = '';
|
||||
event.location = '';
|
||||
event.conferenceSolution = '';
|
||||
event.conferenceUri = '';
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
totalNumberOfCalendarEvents: totalNumberOfCalendarEvents[0].count,
|
||||
timelineCalendarEvents,
|
||||
};
|
||||
}
|
||||
|
||||
async getCalendarEventsFromCompanyId(
|
||||
workspaceMemberId: string,
|
||||
workspaceId: string,
|
||||
companyId: string,
|
||||
page: number = 1,
|
||||
pageSize: number = TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE,
|
||||
): Promise<TimelineCalendarEventsWithTotal> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const personIds = await this.workspaceDataSourceService.executeRawQuery(
|
||||
`
|
||||
SELECT
|
||||
p."id"
|
||||
FROM
|
||||
${dataSourceSchema}."person" p
|
||||
WHERE
|
||||
p."companyId" = $1
|
||||
`,
|
||||
[companyId],
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!personIds) {
|
||||
return {
|
||||
totalNumberOfCalendarEvents: 0,
|
||||
timelineCalendarEvents: [],
|
||||
};
|
||||
}
|
||||
|
||||
const formattedPersonIds = personIds.map(
|
||||
(personId: { id: string }) => personId.id,
|
||||
);
|
||||
|
||||
const messageThreads = await this.getCalendarEventsFromPersonIds(
|
||||
workspaceMemberId,
|
||||
workspaceId,
|
||||
formattedPersonIds,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return messageThreads;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { OpenApiModule } from 'src/engine/modules/open-api/open-api.module';
|
||||
import { TimelineMessagingModule } from 'src/engine/modules/messaging/timeline-messaging.module';
|
||||
import { BillingModule } from 'src/engine/modules/billing/billing.module';
|
||||
import { HealthModule } from 'src/engine/modules/health/health.module';
|
||||
import { TimelineCalendarEventModule } from 'src/engine/modules/calendar/timeline-calendar-event.module';
|
||||
|
||||
import { AnalyticsModule } from './analytics/analytics.module';
|
||||
import { FileModule } from './file/file.module';
|
||||
@@ -26,6 +27,7 @@ import { ClientConfigModule } from './client-config/client-config.module';
|
||||
OpenApiModule,
|
||||
RefreshTokenModule,
|
||||
TimelineMessagingModule,
|
||||
TimelineCalendarEventModule,
|
||||
UserModule,
|
||||
WorkspaceModule,
|
||||
],
|
||||
@@ -34,6 +36,7 @@ import { ClientConfigModule } from './client-config/client-config.module';
|
||||
AuthModule,
|
||||
FeatureFlagModule,
|
||||
TimelineMessagingModule,
|
||||
TimelineCalendarEventModule,
|
||||
UserModule,
|
||||
WorkspaceModule,
|
||||
],
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { ObjectType, Field, ID } from '@nestjs/graphql';
|
||||
|
||||
import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
import { TimelineThreadParticipant } from 'src/engine/modules/messaging/dtos/timeline-thread-participant.dto';
|
||||
|
||||
@ObjectType('TimelineThread')
|
||||
export class TimelineThread {
|
||||
@IDField(() => ID)
|
||||
@Field(() => ID)
|
||||
id: string;
|
||||
|
||||
@Field()
|
||||
|
||||
Reference in New Issue
Block a user