Add JSON field type and Event object (#4566)
* Add JSON field type and Event object * Simplify code * Adress PR comments and add featureFlag
This commit is contained in:
@ -66,10 +66,32 @@ export type AuthTokens = {
|
|||||||
export type Billing = {
|
export type Billing = {
|
||||||
__typename?: 'Billing';
|
__typename?: 'Billing';
|
||||||
billingFreeTrialDurationInDays?: Maybe<Scalars['Float']['output']>;
|
billingFreeTrialDurationInDays?: Maybe<Scalars['Float']['output']>;
|
||||||
billingUrl: Scalars['String']['output'];
|
billingUrl?: Maybe<Scalars['String']['output']>;
|
||||||
isBillingEnabled: Scalars['Boolean']['output'];
|
isBillingEnabled: Scalars['Boolean']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type BillingSubscription = {
|
||||||
|
__typename?: 'BillingSubscription';
|
||||||
|
id: Scalars['ID']['output'];
|
||||||
|
status: Scalars['String']['output'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BillingSubscriptionFilter = {
|
||||||
|
and?: InputMaybe<Array<BillingSubscriptionFilter>>;
|
||||||
|
id?: InputMaybe<IdFilterComparison>;
|
||||||
|
or?: InputMaybe<Array<BillingSubscriptionFilter>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BillingSubscriptionSort = {
|
||||||
|
direction: SortDirection;
|
||||||
|
field: BillingSubscriptionSortFields;
|
||||||
|
nulls?: InputMaybe<SortNulls>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum BillingSubscriptionSortFields {
|
||||||
|
Id = 'id'
|
||||||
|
}
|
||||||
|
|
||||||
export type BooleanFieldComparison = {
|
export type BooleanFieldComparison = {
|
||||||
is?: InputMaybe<Scalars['Boolean']['input']>;
|
is?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
isNot?: InputMaybe<Scalars['Boolean']['input']>;
|
isNot?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
@ -241,6 +263,7 @@ export enum FieldMetadataType {
|
|||||||
DateTime = 'DATE_TIME',
|
DateTime = 'DATE_TIME',
|
||||||
Email = 'EMAIL',
|
Email = 'EMAIL',
|
||||||
FullName = 'FULL_NAME',
|
FullName = 'FULL_NAME',
|
||||||
|
Json = 'JSON',
|
||||||
Link = 'LINK',
|
Link = 'LINK',
|
||||||
MultiSelect = 'MULTI_SELECT',
|
MultiSelect = 'MULTI_SELECT',
|
||||||
Number = 'NUMBER',
|
Number = 'NUMBER',
|
||||||
@ -301,7 +324,6 @@ export type Mutation = {
|
|||||||
activateWorkspace: Workspace;
|
activateWorkspace: Workspace;
|
||||||
challenge: LoginToken;
|
challenge: LoginToken;
|
||||||
checkoutSession: SessionEntity;
|
checkoutSession: SessionEntity;
|
||||||
createEvent: Analytics;
|
|
||||||
createOneField: Field;
|
createOneField: Field;
|
||||||
createOneObject: Object;
|
createOneObject: Object;
|
||||||
createOneRefreshToken: RefreshToken;
|
createOneRefreshToken: RefreshToken;
|
||||||
@ -318,6 +340,7 @@ export type Mutation = {
|
|||||||
impersonate: Verify;
|
impersonate: Verify;
|
||||||
renewToken: AuthTokens;
|
renewToken: AuthTokens;
|
||||||
signUp: LoginToken;
|
signUp: LoginToken;
|
||||||
|
track: Analytics;
|
||||||
updateOneField: Field;
|
updateOneField: Field;
|
||||||
updateOneObject: Object;
|
updateOneObject: Object;
|
||||||
updatePasswordViaResetToken: InvalidatePassword;
|
updatePasswordViaResetToken: InvalidatePassword;
|
||||||
@ -347,12 +370,6 @@ export type MutationCheckoutSessionArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationCreateEventArgs = {
|
|
||||||
data: Scalars['JSON']['input'];
|
|
||||||
type: Scalars['String']['input'];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationCreateOneFieldArgs = {
|
export type MutationCreateOneFieldArgs = {
|
||||||
input: CreateOneFieldMetadataInput;
|
input: CreateOneFieldMetadataInput;
|
||||||
};
|
};
|
||||||
@ -421,6 +438,12 @@ export type MutationSignUpArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationTrackArgs = {
|
||||||
|
data: Scalars['JSON']['input'];
|
||||||
|
type: Scalars['String']['input'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationUpdateOneFieldArgs = {
|
export type MutationUpdateOneFieldArgs = {
|
||||||
input: UpdateOneFieldMetadataInput;
|
input: UpdateOneFieldMetadataInput;
|
||||||
};
|
};
|
||||||
@ -631,6 +654,23 @@ export type RelationConnection = {
|
|||||||
pageInfo: PageInfo;
|
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 = {
|
export type RelationDeleteResponse = {
|
||||||
__typename?: 'RelationDeleteResponse';
|
__typename?: 'RelationDeleteResponse';
|
||||||
createdAt?: Maybe<Scalars['DateTime']['output']>;
|
createdAt?: Maybe<Scalars['DateTime']['output']>;
|
||||||
@ -831,7 +871,9 @@ export type Workspace = {
|
|||||||
__typename?: 'Workspace';
|
__typename?: 'Workspace';
|
||||||
activationStatus: Scalars['String']['output'];
|
activationStatus: Scalars['String']['output'];
|
||||||
allowImpersonation: Scalars['Boolean']['output'];
|
allowImpersonation: Scalars['Boolean']['output'];
|
||||||
|
billingSubscriptions?: Maybe<Array<BillingSubscription>>;
|
||||||
createdAt: Scalars['DateTime']['output'];
|
createdAt: Scalars['DateTime']['output'];
|
||||||
|
currentBillingSubscription?: Maybe<BillingSubscription>;
|
||||||
deletedAt?: Maybe<Scalars['DateTime']['output']>;
|
deletedAt?: Maybe<Scalars['DateTime']['output']>;
|
||||||
displayName?: Maybe<Scalars['String']['output']>;
|
displayName?: Maybe<Scalars['String']['output']>;
|
||||||
domainName?: Maybe<Scalars['String']['output']>;
|
domainName?: Maybe<Scalars['String']['output']>;
|
||||||
@ -844,6 +886,12 @@ export type Workspace = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type WorkspaceBillingSubscriptionsArgs = {
|
||||||
|
filter?: BillingSubscriptionFilter;
|
||||||
|
sorting?: Array<BillingSubscriptionSort>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type WorkspaceFeatureFlagsArgs = {
|
export type WorkspaceFeatureFlagsArgs = {
|
||||||
filter?: FeatureFlagFilter;
|
filter?: FeatureFlagFilter;
|
||||||
sorting?: Array<FeatureFlagSort>;
|
sorting?: Array<FeatureFlagSort>;
|
||||||
@ -886,6 +934,7 @@ export type Field = {
|
|||||||
label: Scalars['String']['output'];
|
label: Scalars['String']['output'];
|
||||||
name: Scalars['String']['output'];
|
name: Scalars['String']['output'];
|
||||||
options?: Maybe<Scalars['JSON']['output']>;
|
options?: Maybe<Scalars['JSON']['output']>;
|
||||||
|
relationDefinition?: Maybe<RelationDefinition>;
|
||||||
toRelationMetadata?: Maybe<Relation>;
|
toRelationMetadata?: Maybe<Relation>;
|
||||||
type: FieldMetadataType;
|
type: FieldMetadataType;
|
||||||
updatedAt: Scalars['DateTime']['output'];
|
updatedAt: Scalars['DateTime']['output'];
|
||||||
|
|||||||
@ -183,6 +183,7 @@ export enum FieldMetadataType {
|
|||||||
DateTime = 'DATE_TIME',
|
DateTime = 'DATE_TIME',
|
||||||
Email = 'EMAIL',
|
Email = 'EMAIL',
|
||||||
FullName = 'FULL_NAME',
|
FullName = 'FULL_NAME',
|
||||||
|
Json = 'JSON',
|
||||||
Link = 'LINK',
|
Link = 'LINK',
|
||||||
MultiSelect = 'MULTI_SELECT',
|
MultiSelect = 'MULTI_SELECT',
|
||||||
Number = 'NUMBER',
|
Number = 'NUMBER',
|
||||||
@ -243,7 +244,6 @@ export type Mutation = {
|
|||||||
activateWorkspace: Workspace;
|
activateWorkspace: Workspace;
|
||||||
challenge: LoginToken;
|
challenge: LoginToken;
|
||||||
checkoutSession: SessionEntity;
|
checkoutSession: SessionEntity;
|
||||||
createEvent: Analytics;
|
|
||||||
createOneObject: Object;
|
createOneObject: Object;
|
||||||
createOneRefreshToken: RefreshToken;
|
createOneRefreshToken: RefreshToken;
|
||||||
deleteCurrentWorkspace: Workspace;
|
deleteCurrentWorkspace: Workspace;
|
||||||
@ -256,6 +256,7 @@ export type Mutation = {
|
|||||||
impersonate: Verify;
|
impersonate: Verify;
|
||||||
renewToken: AuthTokens;
|
renewToken: AuthTokens;
|
||||||
signUp: LoginToken;
|
signUp: LoginToken;
|
||||||
|
track: Analytics;
|
||||||
updateOneObject: Object;
|
updateOneObject: Object;
|
||||||
updatePasswordViaResetToken: InvalidatePassword;
|
updatePasswordViaResetToken: InvalidatePassword;
|
||||||
updateWorkspace: Workspace;
|
updateWorkspace: Workspace;
|
||||||
@ -284,12 +285,6 @@ export type MutationCheckoutSessionArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationCreateEventArgs = {
|
|
||||||
data: Scalars['JSON'];
|
|
||||||
type: Scalars['String'];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationDeleteOneObjectArgs = {
|
export type MutationDeleteOneObjectArgs = {
|
||||||
input: DeleteOneObjectInput;
|
input: DeleteOneObjectInput;
|
||||||
};
|
};
|
||||||
@ -328,6 +323,12 @@ export type MutationSignUpArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationTrackArgs = {
|
||||||
|
data: Scalars['JSON'];
|
||||||
|
type: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationUpdatePasswordViaResetTokenArgs = {
|
export type MutationUpdatePasswordViaResetTokenArgs = {
|
||||||
newPassword: Scalars['String'];
|
newPassword: Scalars['String'];
|
||||||
passwordResetToken: Scalars['String'];
|
passwordResetToken: Scalars['String'];
|
||||||
@ -917,13 +918,13 @@ export type GetTimelineThreadsFromPersonIdQuery = { __typename?: 'Query', getTim
|
|||||||
|
|
||||||
export type TimelineThreadFragment = { __typename?: 'TimelineThread', id: string, subject: string, lastMessageReceivedAt: string };
|
export type TimelineThreadFragment = { __typename?: 'TimelineThread', id: string, subject: string, lastMessageReceivedAt: string };
|
||||||
|
|
||||||
export type CreateEventMutationVariables = Exact<{
|
export type TrackMutationVariables = Exact<{
|
||||||
type: Scalars['String'];
|
type: Scalars['String'];
|
||||||
data: Scalars['JSON'];
|
data: Scalars['JSON'];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type CreateEventMutation = { __typename?: 'Mutation', createEvent: { __typename?: 'Analytics', success: boolean } };
|
export type TrackMutation = { __typename?: 'Mutation', track: { __typename?: 'Analytics', success: boolean } };
|
||||||
|
|
||||||
export type AuthTokenFragmentFragment = { __typename?: 'AuthToken', token: string, expiresAt: string };
|
export type AuthTokenFragmentFragment = { __typename?: 'AuthToken', token: string, expiresAt: string };
|
||||||
|
|
||||||
@ -1397,40 +1398,40 @@ export function useGetTimelineThreadsFromPersonIdLazyQuery(baseOptions?: Apollo.
|
|||||||
export type GetTimelineThreadsFromPersonIdQueryHookResult = ReturnType<typeof useGetTimelineThreadsFromPersonIdQuery>;
|
export type GetTimelineThreadsFromPersonIdQueryHookResult = ReturnType<typeof useGetTimelineThreadsFromPersonIdQuery>;
|
||||||
export type GetTimelineThreadsFromPersonIdLazyQueryHookResult = ReturnType<typeof useGetTimelineThreadsFromPersonIdLazyQuery>;
|
export type GetTimelineThreadsFromPersonIdLazyQueryHookResult = ReturnType<typeof useGetTimelineThreadsFromPersonIdLazyQuery>;
|
||||||
export type GetTimelineThreadsFromPersonIdQueryResult = Apollo.QueryResult<GetTimelineThreadsFromPersonIdQuery, GetTimelineThreadsFromPersonIdQueryVariables>;
|
export type GetTimelineThreadsFromPersonIdQueryResult = Apollo.QueryResult<GetTimelineThreadsFromPersonIdQuery, GetTimelineThreadsFromPersonIdQueryVariables>;
|
||||||
export const CreateEventDocument = gql`
|
export const TrackDocument = gql`
|
||||||
mutation CreateEvent($type: String!, $data: JSON!) {
|
mutation Track($type: String!, $data: JSON!) {
|
||||||
createEvent(type: $type, data: $data) {
|
track(type: $type, data: $data) {
|
||||||
success
|
success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export type CreateEventMutationFn = Apollo.MutationFunction<CreateEventMutation, CreateEventMutationVariables>;
|
export type TrackMutationFn = Apollo.MutationFunction<TrackMutation, TrackMutationVariables>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __useCreateEventMutation__
|
* __useTrackMutation__
|
||||||
*
|
*
|
||||||
* To run a mutation, you first call `useCreateEventMutation` within a React component and pass it any options that fit your needs.
|
* To run a mutation, you first call `useTrackMutation` within a React component and pass it any options that fit your needs.
|
||||||
* When your component renders, `useCreateEventMutation` returns a tuple that includes:
|
* When your component renders, `useTrackMutation` returns a tuple that includes:
|
||||||
* - A mutate function that you can call at any time to execute the mutation
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
* - An object with fields that represent the current status of the mutation's execution
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
*
|
*
|
||||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const [createEventMutation, { data, loading, error }] = useCreateEventMutation({
|
* const [trackMutation, { data, loading, error }] = useTrackMutation({
|
||||||
* variables: {
|
* variables: {
|
||||||
* type: // value for 'type'
|
* type: // value for 'type'
|
||||||
* data: // value for 'data'
|
* data: // value for 'data'
|
||||||
* },
|
* },
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
export function useCreateEventMutation(baseOptions?: Apollo.MutationHookOptions<CreateEventMutation, CreateEventMutationVariables>) {
|
export function useTrackMutation(baseOptions?: Apollo.MutationHookOptions<TrackMutation, TrackMutationVariables>) {
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
return Apollo.useMutation<CreateEventMutation, CreateEventMutationVariables>(CreateEventDocument, options);
|
return Apollo.useMutation<TrackMutation, TrackMutationVariables>(TrackDocument, options);
|
||||||
}
|
}
|
||||||
export type CreateEventMutationHookResult = ReturnType<typeof useCreateEventMutation>;
|
export type TrackMutationHookResult = ReturnType<typeof useTrackMutation>;
|
||||||
export type CreateEventMutationResult = Apollo.MutationResult<CreateEventMutation>;
|
export type TrackMutationResult = Apollo.MutationResult<TrackMutation>;
|
||||||
export type CreateEventMutationOptions = Apollo.BaseMutationOptions<CreateEventMutation, CreateEventMutationVariables>;
|
export type TrackMutationOptions = Apollo.BaseMutationOptions<TrackMutation, TrackMutationVariables>;
|
||||||
export const ChallengeDocument = gql`
|
export const ChallengeDocument = gql`
|
||||||
mutation Challenge($email: String!, $password: String!) {
|
mutation Challenge($email: String!, $password: String!) {
|
||||||
challenge(email: $email, password: $password) {
|
challenge(email: $email, password: $password) {
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
import { gql } from '@apollo/client';
|
|
||||||
|
|
||||||
export const CREATE_EVENT = gql`
|
|
||||||
mutation CreateEvent($type: String!, $data: JSON!) {
|
|
||||||
createEvent(type: $type, data: $data) {
|
|
||||||
success
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const TRACK = gql`
|
||||||
|
mutation Track($type: String!, $data: JSON!) {
|
||||||
|
track(type: $type, data: $data) {
|
||||||
|
success
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@ -11,8 +11,8 @@ const mocks: MockedResponse[] = [
|
|||||||
{
|
{
|
||||||
request: {
|
request: {
|
||||||
query: gql`
|
query: gql`
|
||||||
mutation CreateEvent($type: String!, $data: JSON!) {
|
mutation Track($type: String!, $data: JSON!) {
|
||||||
createEvent(type: $type, data: $data) {
|
track(type: $type, data: $data) {
|
||||||
success
|
success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -24,7 +24,7 @@ const mocks: MockedResponse[] = [
|
|||||||
},
|
},
|
||||||
result: jest.fn(() => ({
|
result: jest.fn(() => ({
|
||||||
data: {
|
data: {
|
||||||
createEvent: {
|
track: {
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -4,10 +4,10 @@ import { RecoilRoot } from 'recoil';
|
|||||||
|
|
||||||
import { useTrackEvent } from '../useTrackEvent';
|
import { useTrackEvent } from '../useTrackEvent';
|
||||||
|
|
||||||
const mockCreateEventMutation = jest.fn();
|
const mockTrackMutation = jest.fn();
|
||||||
|
|
||||||
jest.mock('~/generated/graphql', () => ({
|
jest.mock('~/generated/graphql', () => ({
|
||||||
useCreateEventMutation: () => [mockCreateEventMutation],
|
useTrackMutation: () => [mockTrackMutation],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('useTrackEvent', () => {
|
describe('useTrackEvent', () => {
|
||||||
@ -17,8 +17,8 @@ describe('useTrackEvent', () => {
|
|||||||
renderHook(() => useTrackEvent(eventType, eventData), {
|
renderHook(() => useTrackEvent(eventType, eventData), {
|
||||||
wrapper: RecoilRoot,
|
wrapper: RecoilRoot,
|
||||||
});
|
});
|
||||||
expect(mockCreateEventMutation).toHaveBeenCalledTimes(1);
|
expect(mockTrackMutation).toHaveBeenCalledTimes(1);
|
||||||
expect(mockCreateEventMutation).toHaveBeenCalledWith({
|
expect(mockTrackMutation).toHaveBeenCalledWith({
|
||||||
variables: { type: eventType, data: eventData },
|
variables: { type: eventType, data: eventData },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useCallback } from 'react';
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { telemetryState } from '@/client-config/states/telemetryState';
|
import { telemetryState } from '@/client-config/states/telemetryState';
|
||||||
import { useCreateEventMutation } from '~/generated/graphql';
|
import { useTrackMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
interface EventLocation {
|
interface EventLocation {
|
||||||
pathname: string;
|
pathname: string;
|
||||||
@ -14,7 +14,7 @@ export interface EventData {
|
|||||||
|
|
||||||
export const useEventTracker = () => {
|
export const useEventTracker = () => {
|
||||||
const telemetry = useRecoilValue(telemetryState());
|
const telemetry = useRecoilValue(telemetryState());
|
||||||
const [createEventMutation] = useCreateEventMutation();
|
const [createEventMutation] = useTrackMutation();
|
||||||
|
|
||||||
return useCallback(
|
return useCallback(
|
||||||
(eventType: string, eventData: EventData) => {
|
(eventType: string, eventData: EventData) => {
|
||||||
|
|||||||
@ -77,8 +77,8 @@ describe('useApolloFactory', () => {
|
|||||||
await act(async () => {
|
await act(async () => {
|
||||||
await result.current.factory.mutate({
|
await result.current.factory.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
mutation CreateEvent($type: String!, $data: JSON!) {
|
mutation Track($type: String!, $data: JSON!) {
|
||||||
createEvent(type: $type, data: $data) {
|
track(type: $type, data: $data) {
|
||||||
success
|
success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,8 +41,8 @@ const makeRequest = async () => {
|
|||||||
|
|
||||||
await client.mutate({
|
await client.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
mutation CreateEvent($type: String!, $data: JSON!) {
|
mutation Track($type: String!, $data: JSON!) {
|
||||||
createEvent(type: $type, data: $data) {
|
track(type: $type, data: $data) {
|
||||||
success
|
success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
export type FeatureFlagKey =
|
export type FeatureFlagKey =
|
||||||
| 'IS_BLOCKLIST_ENABLED'
|
| 'IS_BLOCKLIST_ENABLED'
|
||||||
| 'IS_CALENDAR_ENABLED'
|
| 'IS_CALENDAR_ENABLED'
|
||||||
| 'IS_QUICK_ACTIONS_ENABLED';
|
| 'IS_QUICK_ACTIONS_ENABLED'
|
||||||
|
| 'IS_EVENT_OBJECT_ENABLED';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { getOperationName } from '@apollo/client/utilities';
|
import { getOperationName } from '@apollo/client/utilities';
|
||||||
import { graphql, HttpResponse } from 'msw';
|
import { graphql, HttpResponse } from 'msw';
|
||||||
|
|
||||||
import { CREATE_EVENT } from '@/analytics/graphql/queries/createEvent';
|
import { TRACK } from '@/analytics/graphql/queries/track';
|
||||||
import { GET_CLIENT_CONFIG } from '@/client-config/graphql/queries/getClientConfig';
|
import { GET_CLIENT_CONFIG } from '@/client-config/graphql/queries/getClientConfig';
|
||||||
import { FIND_MANY_OBJECT_METADATA_ITEMS } from '@/object-metadata/graphql/queries';
|
import { FIND_MANY_OBJECT_METADATA_ITEMS } from '@/object-metadata/graphql/queries';
|
||||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||||
@ -31,10 +31,10 @@ export const graphqlMocks = {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
graphql.mutation(getOperationName(CREATE_EVENT) ?? '', () => {
|
graphql.mutation(getOperationName(TRACK) ?? '', () => {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
data: {
|
data: {
|
||||||
createEvent: { success: 1, __typename: 'Event' },
|
track: { success: 1, __typename: 'TRACK' },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -25,6 +25,11 @@ export const seedFeatureFlags = async (
|
|||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: FeatureFlagKeys.IsEventObjectEnabled,
|
||||||
|
workspaceId: workspaceId,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
])
|
])
|
||||||
.execute();
|
.execute();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import {
|
|||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsDate,
|
IsDate,
|
||||||
|
IsJSON,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsNumber,
|
IsNumber,
|
||||||
IsNumberString,
|
IsNumberString,
|
||||||
@ -16,6 +17,12 @@ export class FieldMetadataDefaultValueString {
|
|||||||
value: string | null;
|
value: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FieldMetadataDefaultValueJson {
|
||||||
|
@ValidateIf((_object, value) => value !== null)
|
||||||
|
@IsJSON()
|
||||||
|
value: JSON | null;
|
||||||
|
}
|
||||||
|
|
||||||
export class FieldMetadataDefaultValueNumber {
|
export class FieldMetadataDefaultValueNumber {
|
||||||
@ValidateIf((_object, value) => value !== null)
|
@ValidateIf((_object, value) => value !== null)
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
|
|||||||
@ -36,6 +36,7 @@ export enum FieldMetadataType {
|
|||||||
MULTI_SELECT = 'MULTI_SELECT',
|
MULTI_SELECT = 'MULTI_SELECT',
|
||||||
RELATION = 'RELATION',
|
RELATION = 'RELATION',
|
||||||
POSITION = 'POSITION',
|
POSITION = 'POSITION',
|
||||||
|
JSON = 'JSON',
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity('fieldMetadata')
|
@Entity('fieldMetadata')
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import {
|
|||||||
FieldMetadataDefaultValueCurrency,
|
FieldMetadataDefaultValueCurrency,
|
||||||
FieldMetadataDefaultValueDateTime,
|
FieldMetadataDefaultValueDateTime,
|
||||||
FieldMetadataDefaultValueFullName,
|
FieldMetadataDefaultValueFullName,
|
||||||
|
FieldMetadataDefaultValueJson,
|
||||||
FieldMetadataDefaultValueLink,
|
FieldMetadataDefaultValueLink,
|
||||||
FieldMetadataDefaultValueNumber,
|
FieldMetadataDefaultValueNumber,
|
||||||
FieldMetadataDefaultValueString,
|
FieldMetadataDefaultValueString,
|
||||||
@ -50,6 +51,7 @@ type FieldMetadataDefaultValueMapping = {
|
|||||||
[FieldMetadataType.RATING]: FieldMetadataDefaultValueString;
|
[FieldMetadataType.RATING]: FieldMetadataDefaultValueString;
|
||||||
[FieldMetadataType.SELECT]: FieldMetadataDefaultValueString;
|
[FieldMetadataType.SELECT]: FieldMetadataDefaultValueString;
|
||||||
[FieldMetadataType.MULTI_SELECT]: FieldMetadataDefaultValueStringArray;
|
[FieldMetadataType.MULTI_SELECT]: FieldMetadataDefaultValueStringArray;
|
||||||
|
[FieldMetadataType.JSON]: FieldMetadataDefaultValueJson;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DefaultValueByFieldMetadata<T extends FieldMetadataType | 'default'> = [
|
type DefaultValueByFieldMetadata<T extends FieldMetadataType | 'default'> = [
|
||||||
|
|||||||
@ -35,6 +35,7 @@ export function generateTargetColumnMap(
|
|||||||
case FieldMetadataType.SELECT:
|
case FieldMetadataType.SELECT:
|
||||||
case FieldMetadataType.MULTI_SELECT:
|
case FieldMetadataType.MULTI_SELECT:
|
||||||
case FieldMetadataType.POSITION:
|
case FieldMetadataType.POSITION:
|
||||||
|
case FieldMetadataType.JSON:
|
||||||
return {
|
return {
|
||||||
value: columnName,
|
value: columnName,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
FieldMetadataDefaultValueCurrency,
|
FieldMetadataDefaultValueCurrency,
|
||||||
FieldMetadataDefaultValueDateTime,
|
FieldMetadataDefaultValueDateTime,
|
||||||
FieldMetadataDefaultValueFullName,
|
FieldMetadataDefaultValueFullName,
|
||||||
|
FieldMetadataDefaultValueJson,
|
||||||
FieldMetadataDefaultValueLink,
|
FieldMetadataDefaultValueLink,
|
||||||
FieldMetadataDefaultValueNumber,
|
FieldMetadataDefaultValueNumber,
|
||||||
FieldMetadataDefaultValueString,
|
FieldMetadataDefaultValueString,
|
||||||
@ -39,6 +40,7 @@ export const defaultValueValidatorsMap = {
|
|||||||
[FieldMetadataType.RATING]: [FieldMetadataDefaultValueString],
|
[FieldMetadataType.RATING]: [FieldMetadataDefaultValueString],
|
||||||
[FieldMetadataType.SELECT]: [FieldMetadataDefaultValueString],
|
[FieldMetadataType.SELECT]: [FieldMetadataDefaultValueString],
|
||||||
[FieldMetadataType.MULTI_SELECT]: [FieldMetadataDefaultValueStringArray],
|
[FieldMetadataType.MULTI_SELECT]: [FieldMetadataDefaultValueStringArray],
|
||||||
|
[FieldMetadataType.JSON]: [FieldMetadataDefaultValueJson],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const validateDefaultValueForType = (
|
export const validateDefaultValueForType = (
|
||||||
|
|||||||
@ -29,6 +29,8 @@ export const fieldMetadataTypeToColumnType = <Type extends FieldMetadataType>(
|
|||||||
case FieldMetadataType.SELECT:
|
case FieldMetadataType.SELECT:
|
||||||
case FieldMetadataType.MULTI_SELECT:
|
case FieldMetadataType.MULTI_SELECT:
|
||||||
return 'enum';
|
return 'enum';
|
||||||
|
case FieldMetadataType.JSON:
|
||||||
|
return 'jsonb';
|
||||||
default:
|
default:
|
||||||
throw new Error(`Cannot convert ${fieldMetadataType} to column type.`);
|
throw new Error(`Cannot convert ${fieldMetadataType} to column type.`);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,6 +67,7 @@ export class WorkspaceMigrationFactory {
|
|||||||
[FieldMetadataType.NUMERIC, { factory: this.basicColumnActionFactory }],
|
[FieldMetadataType.NUMERIC, { factory: this.basicColumnActionFactory }],
|
||||||
[FieldMetadataType.NUMBER, { factory: this.basicColumnActionFactory }],
|
[FieldMetadataType.NUMBER, { factory: this.basicColumnActionFactory }],
|
||||||
[FieldMetadataType.POSITION, { factory: this.basicColumnActionFactory }],
|
[FieldMetadataType.POSITION, { factory: this.basicColumnActionFactory }],
|
||||||
|
[FieldMetadataType.JSON, { factory: this.basicColumnActionFactory }],
|
||||||
[
|
[
|
||||||
FieldMetadataType.PROBABILITY,
|
FieldMetadataType.PROBABILITY,
|
||||||
{ factory: this.basicColumnActionFactory },
|
{ factory: this.basicColumnActionFactory },
|
||||||
|
|||||||
@ -0,0 +1,54 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { MessageQueueJob } from 'src/engine/integrations/message-queue/interfaces/message-queue-job.interface';
|
||||||
|
|
||||||
|
import { DataSourceService } from 'src/engine-metadata/data-source/data-source.service';
|
||||||
|
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||||
|
|
||||||
|
export type SaveEventToDbJobData = {
|
||||||
|
workspaceId: string;
|
||||||
|
recordId: string;
|
||||||
|
objectName: string;
|
||||||
|
operation: string;
|
||||||
|
details: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SaveEventToDbJob implements MessageQueueJob<SaveEventToDbJobData> {
|
||||||
|
constructor(
|
||||||
|
private readonly dataSourceService: DataSourceService,
|
||||||
|
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async handle(data: SaveEventToDbJobData): Promise<void> {
|
||||||
|
const dataSourceMetadata =
|
||||||
|
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||||
|
data.workspaceId,
|
||||||
|
);
|
||||||
|
const workspaceDataSource =
|
||||||
|
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
||||||
|
data.workspaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const eventType = `${data.operation}.${data.objectName}`;
|
||||||
|
|
||||||
|
// TODO: add "workspaceMember" (who performed the action, need to send it in the event)
|
||||||
|
// TODO: need to support objects others than "person", "company", "opportunities"
|
||||||
|
|
||||||
|
if (
|
||||||
|
data.objectName != 'person' &&
|
||||||
|
data.objectName != 'company' &&
|
||||||
|
data.objectName != 'opportunities'
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await workspaceDataSource?.query(
|
||||||
|
`INSERT INTO ${dataSourceMetadata.schema}."event"
|
||||||
|
("name", "properties", "${data.objectName}Id")
|
||||||
|
VALUES ('${eventType}', '${JSON.stringify(data.details)}', '${
|
||||||
|
data.recordId
|
||||||
|
}') RETURNING *`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
||||||
|
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
|
||||||
|
import { ObjectRecordCreateEvent } from 'src/engine/integrations/event-emitter/types/object-record-create.event';
|
||||||
|
import {
|
||||||
|
SaveEventToDbJobData,
|
||||||
|
SaveEventToDbJob,
|
||||||
|
} from 'src/engine/api/graphql/workspace-query-runner/jobs/save-event-to-db.job';
|
||||||
|
import {
|
||||||
|
FeatureFlagEntity,
|
||||||
|
FeatureFlagKeys,
|
||||||
|
} from 'src/engine/modules/feature-flag/feature-flag.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EntityEventsToDbListener {
|
||||||
|
constructor(
|
||||||
|
@Inject(MessageQueue.entityEventsToDbQueue)
|
||||||
|
private readonly messageQueueService: MessageQueueService,
|
||||||
|
@InjectRepository(FeatureFlagEntity, 'core')
|
||||||
|
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@OnEvent('*.created')
|
||||||
|
async handleCreate(payload: ObjectRecordCreateEvent<any>) {
|
||||||
|
return this.handle(payload, 'created');
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnEvent('*.updated')
|
||||||
|
async handleUpdate(payload: ObjectRecordCreateEvent<any>) {
|
||||||
|
return this.handle(payload, 'updated');
|
||||||
|
}
|
||||||
|
|
||||||
|
// @OnEvent('*.deleted') - TODO: implement when we have soft deleted
|
||||||
|
// ....
|
||||||
|
|
||||||
|
private async handle(
|
||||||
|
payload: ObjectRecordCreateEvent<any>,
|
||||||
|
operation: string,
|
||||||
|
) {
|
||||||
|
const isEventObjectEnabledFeatureFlag =
|
||||||
|
await this.featureFlagRepository.findOneBy({
|
||||||
|
workspaceId: payload.workspaceId,
|
||||||
|
key: FeatureFlagKeys.IsEventObjectEnabled,
|
||||||
|
value: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isEventObjectEnabledFeatureFlag ||
|
||||||
|
!isEventObjectEnabledFeatureFlag.value
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messageQueueService.add<SaveEventToDbJobData>(SaveEventToDbJob.name, {
|
||||||
|
workspaceId: payload.workspaceId,
|
||||||
|
recordId: payload.recordId,
|
||||||
|
objectName: payload.objectMetadata.nameSingular,
|
||||||
|
operation: operation,
|
||||||
|
details: payload.details,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +1,9 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { OnEvent } from '@nestjs/event-emitter';
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
|
||||||
import {
|
import { ObjectMetadataInterface } from 'src/engine-metadata/field-metadata/interfaces/object-metadata.interface';
|
||||||
CreatedObjectMetadata,
|
|
||||||
ObjectRecordCreateEvent,
|
import { ObjectRecordCreateEvent } from 'src/engine/integrations/event-emitter/types/object-record-create.event';
|
||||||
} from 'src/engine/integrations/event-emitter/types/object-record-create.event';
|
|
||||||
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
||||||
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
|
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
|
||||||
import {
|
import {
|
||||||
@ -21,11 +20,11 @@ export class RecordPositionListener {
|
|||||||
|
|
||||||
@OnEvent('*.created')
|
@OnEvent('*.created')
|
||||||
async handleAllCreate(payload: ObjectRecordCreateEvent<any>) {
|
async handleAllCreate(payload: ObjectRecordCreateEvent<any>) {
|
||||||
if (!hasPositionField(payload.createdObjectMetadata)) {
|
if (!hasPositionField(payload.objectMetadata)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasPositionSet(payload.createdRecord)) {
|
if (hasPositionSet(payload.details.after)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,15 +32,19 @@ export class RecordPositionListener {
|
|||||||
RecordPositionBackfillJob.name,
|
RecordPositionBackfillJob.name,
|
||||||
{
|
{
|
||||||
workspaceId: payload.workspaceId,
|
workspaceId: payload.workspaceId,
|
||||||
recordId: payload.createdRecord.id,
|
recordId: payload.recordId,
|
||||||
objectMetadata: payload.createdObjectMetadata,
|
objectMetadata: {
|
||||||
|
nameSingular: payload.objectMetadata.nameSingular,
|
||||||
|
isCustom: payload.objectMetadata.isCustom,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: use objectMetadata instead of hardcoded standard objects name
|
||||||
const hasPositionField = (
|
const hasPositionField = (
|
||||||
createdObjectMetadata: CreatedObjectMetadata,
|
createdObjectMetadata: ObjectMetadataInterface,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
return (
|
return (
|
||||||
createdObjectMetadata.isCustom ||
|
createdObjectMetadata.isCustom ||
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { WorkspaceQueryBuilderModule } from 'src/engine/api/graphql/workspace-query-builder/workspace-query-builder.module';
|
import { WorkspaceQueryBuilderModule } from 'src/engine/api/graphql/workspace-query-builder/workspace-query-builder.module';
|
||||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||||
@ -6,20 +7,26 @@ import { WorkspacePreQueryHookModule } from 'src/engine/api/graphql/workspace-qu
|
|||||||
import { workspaceQueryRunnerFactories } from 'src/engine/api/graphql/workspace-query-runner/factories';
|
import { workspaceQueryRunnerFactories } from 'src/engine/api/graphql/workspace-query-runner/factories';
|
||||||
import { RecordPositionListener } from 'src/engine/api/graphql/workspace-query-runner/listeners/record-position.listener';
|
import { RecordPositionListener } from 'src/engine/api/graphql/workspace-query-runner/listeners/record-position.listener';
|
||||||
import { AuthModule } from 'src/engine/modules/auth/auth.module';
|
import { AuthModule } from 'src/engine/modules/auth/auth.module';
|
||||||
|
import { FeatureFlagEntity } from 'src/engine/modules/feature-flag/feature-flag.entity';
|
||||||
|
import { Workspace } from 'src/engine/modules/workspace/workspace.entity';
|
||||||
|
|
||||||
import { WorkspaceQueryRunnerService } from './workspace-query-runner.service';
|
import { WorkspaceQueryRunnerService } from './workspace-query-runner.service';
|
||||||
|
|
||||||
|
import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listener';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
AuthModule,
|
AuthModule,
|
||||||
WorkspaceQueryBuilderModule,
|
WorkspaceQueryBuilderModule,
|
||||||
WorkspaceDataSourceModule,
|
WorkspaceDataSourceModule,
|
||||||
WorkspacePreQueryHookModule,
|
WorkspacePreQueryHookModule,
|
||||||
|
TypeOrmModule.forFeature([Workspace, FeatureFlagEntity], 'core'),
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
WorkspaceQueryRunnerService,
|
WorkspaceQueryRunnerService,
|
||||||
...workspaceQueryRunnerFactories,
|
...workspaceQueryRunnerFactories,
|
||||||
RecordPositionListener,
|
RecordPositionListener,
|
||||||
|
EntityEventsToDbListener,
|
||||||
],
|
],
|
||||||
exports: [WorkspaceQueryRunnerService],
|
exports: [WorkspaceQueryRunnerService],
|
||||||
})
|
})
|
||||||
|
|||||||
@ -246,10 +246,10 @@ export class WorkspaceQueryRunnerService {
|
|||||||
parsedResults.forEach((record) => {
|
parsedResults.forEach((record) => {
|
||||||
this.eventEmitter.emit(`${objectMetadataItem.nameSingular}.created`, {
|
this.eventEmitter.emit(`${objectMetadataItem.nameSingular}.created`, {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
createdRecord: this.removeNestedProperties(record),
|
recordId: record.id,
|
||||||
createdObjectMetadata: {
|
objectMetadata: objectMetadataItem,
|
||||||
nameSingular: objectMetadataItem.nameSingular,
|
details: {
|
||||||
isCustom: objectMetadataItem.isCustom,
|
after: record,
|
||||||
},
|
},
|
||||||
} satisfies ObjectRecordCreateEvent<any>);
|
} satisfies ObjectRecordCreateEvent<any>);
|
||||||
});
|
});
|
||||||
@ -300,8 +300,12 @@ export class WorkspaceQueryRunnerService {
|
|||||||
|
|
||||||
this.eventEmitter.emit(`${objectMetadataItem.nameSingular}.updated`, {
|
this.eventEmitter.emit(`${objectMetadataItem.nameSingular}.updated`, {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
previousRecord: this.removeNestedProperties(existingRecord as Record),
|
recordId: (existingRecord as Record).id,
|
||||||
updatedRecord: this.removeNestedProperties(parsedResults?.[0]),
|
objectMetadata: objectMetadataItem,
|
||||||
|
details: {
|
||||||
|
before: this.removeNestedProperties(existingRecord as Record),
|
||||||
|
after: this.removeNestedProperties(parsedResults?.[0]),
|
||||||
|
},
|
||||||
} satisfies ObjectRecordUpdateEvent<any>);
|
} satisfies ObjectRecordUpdateEvent<any>);
|
||||||
|
|
||||||
return parsedResults?.[0];
|
return parsedResults?.[0];
|
||||||
@ -336,6 +340,12 @@ export class WorkspaceQueryRunnerService {
|
|||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO: check - NO EVENT SENT?
|
||||||
|
// OK I spent 2 hours trying to implement before/after diff and
|
||||||
|
// figured out why it hasn't been implement
|
||||||
|
// Doing a findMany in that context is very hard as long as we don't
|
||||||
|
// have a proper ORM. Let's come back to this once we do (target end of April 24?)
|
||||||
|
|
||||||
return parsedResults;
|
return parsedResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,7 +384,11 @@ export class WorkspaceQueryRunnerService {
|
|||||||
parsedResults.forEach((record) => {
|
parsedResults.forEach((record) => {
|
||||||
this.eventEmitter.emit(`${objectMetadataItem.nameSingular}.deleted`, {
|
this.eventEmitter.emit(`${objectMetadataItem.nameSingular}.deleted`, {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
deletedRecord: [this.removeNestedProperties(record)],
|
recordId: record.id,
|
||||||
|
objectMetadata: objectMetadataItem,
|
||||||
|
details: {
|
||||||
|
before: [this.removeNestedProperties(record)],
|
||||||
|
},
|
||||||
} satisfies ObjectRecordDeleteEvent<any>);
|
} satisfies ObjectRecordDeleteEvent<any>);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -408,7 +422,11 @@ export class WorkspaceQueryRunnerService {
|
|||||||
|
|
||||||
this.eventEmitter.emit(`${objectMetadataItem.nameSingular}.deleted`, {
|
this.eventEmitter.emit(`${objectMetadataItem.nameSingular}.deleted`, {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
deletedRecord: this.removeNestedProperties(parsedResults?.[0]),
|
recordId: args.id,
|
||||||
|
objectMetadata: objectMetadataItem,
|
||||||
|
details: {
|
||||||
|
before: this.removeNestedProperties(parsedResults?.[0]),
|
||||||
|
},
|
||||||
} satisfies ObjectRecordDeleteEvent<any>);
|
} satisfies ObjectRecordDeleteEvent<any>);
|
||||||
|
|
||||||
return parsedResults?.[0];
|
return parsedResults?.[0];
|
||||||
|
|||||||
@ -8,3 +8,4 @@ export * from './string-filter.input-type';
|
|||||||
export * from './time-filter.input-type';
|
export * from './time-filter.input-type';
|
||||||
export * from './uuid-filter.input-type';
|
export * from './uuid-filter.input-type';
|
||||||
export * from './boolean-filter.input-type';
|
export * from './boolean-filter.input-type';
|
||||||
|
export * from './json-filter.input-type';
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { GraphQLInputObjectType } from 'graphql';
|
||||||
|
|
||||||
|
import { FilterIs } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||||
|
|
||||||
|
export const JsonFilterType = new GraphQLInputObjectType({
|
||||||
|
name: 'JsonFilter',
|
||||||
|
fields: {
|
||||||
|
is: { type: FilterIs },
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -15,6 +15,7 @@ import {
|
|||||||
GraphQLString,
|
GraphQLString,
|
||||||
GraphQLType,
|
GraphQLType,
|
||||||
} from 'graphql';
|
} from 'graphql';
|
||||||
|
import GraphQLJSON from 'graphql-type-json';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DateScalarMode,
|
DateScalarMode,
|
||||||
@ -31,6 +32,7 @@ import {
|
|||||||
IntFilterType,
|
IntFilterType,
|
||||||
BooleanFilterType,
|
BooleanFilterType,
|
||||||
BigFloatFilterType,
|
BigFloatFilterType,
|
||||||
|
JsonFilterType,
|
||||||
} from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input';
|
} from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input';
|
||||||
import { OrderByDirectionType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/enum';
|
import { OrderByDirectionType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/enum';
|
||||||
import { BigFloatScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
import { BigFloatScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||||
@ -68,6 +70,7 @@ export class TypeMapperService {
|
|||||||
[FieldMetadataType.PROBABILITY, GraphQLFloat],
|
[FieldMetadataType.PROBABILITY, GraphQLFloat],
|
||||||
[FieldMetadataType.RELATION, GraphQLID],
|
[FieldMetadataType.RELATION, GraphQLID],
|
||||||
[FieldMetadataType.POSITION, PositionScalarType],
|
[FieldMetadataType.POSITION, PositionScalarType],
|
||||||
|
[FieldMetadataType.JSON, GraphQLJSON],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return typeScalarMapping.get(fieldMetadataType);
|
return typeScalarMapping.get(fieldMetadataType);
|
||||||
@ -99,6 +102,7 @@ export class TypeMapperService {
|
|||||||
[FieldMetadataType.PROBABILITY, FloatFilterType],
|
[FieldMetadataType.PROBABILITY, FloatFilterType],
|
||||||
[FieldMetadataType.RELATION, UUIDFilterType],
|
[FieldMetadataType.RELATION, UUIDFilterType],
|
||||||
[FieldMetadataType.POSITION, FloatFilterType],
|
[FieldMetadataType.POSITION, FloatFilterType],
|
||||||
|
[FieldMetadataType.JSON, JsonFilterType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return typeFilterMapping.get(fieldMetadataType);
|
return typeFilterMapping.get(fieldMetadataType);
|
||||||
@ -122,6 +126,7 @@ export class TypeMapperService {
|
|||||||
[FieldMetadataType.SELECT, OrderByDirectionType],
|
[FieldMetadataType.SELECT, OrderByDirectionType],
|
||||||
[FieldMetadataType.MULTI_SELECT, OrderByDirectionType],
|
[FieldMetadataType.MULTI_SELECT, OrderByDirectionType],
|
||||||
[FieldMetadataType.POSITION, OrderByDirectionType],
|
[FieldMetadataType.POSITION, OrderByDirectionType],
|
||||||
|
[FieldMetadataType.JSON, OrderByDirectionType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return typeOrderByMapping.get(fieldMetadataType);
|
return typeOrderByMapping.get(fieldMetadataType);
|
||||||
|
|||||||
@ -1,12 +1,7 @@
|
|||||||
import { BaseObjectMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects/base.object-metadata';
|
import { ObjectRecordBaseEvent } from 'src/engine/integrations/event-emitter/types/object-record.base.event';
|
||||||
|
|
||||||
export type CreatedObjectMetadata = {
|
export class ObjectRecordCreateEvent<T> extends ObjectRecordBaseEvent {
|
||||||
nameSingular: string;
|
details: {
|
||||||
isCustom: boolean;
|
after: T;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ObjectRecordCreateEvent<T extends BaseObjectMetadata> {
|
|
||||||
workspaceId: string;
|
|
||||||
createdRecord: T;
|
|
||||||
createdObjectMetadata: CreatedObjectMetadata;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { BaseObjectMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects/base.object-metadata';
|
import { ObjectRecordBaseEvent } from 'src/engine/integrations/event-emitter/types/object-record.base.event';
|
||||||
|
|
||||||
export declare class ObjectRecordDeleteEvent<T extends BaseObjectMetadata> {
|
export class ObjectRecordDeleteEvent<T> extends ObjectRecordBaseEvent {
|
||||||
workspaceId: string;
|
details: {
|
||||||
deletedRecord: T;
|
before: T;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { BaseObjectMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects/base.object-metadata';
|
import { ObjectRecordBaseEvent } from 'src/engine/integrations/event-emitter/types/object-record.base.event';
|
||||||
|
|
||||||
export class ObjectRecordUpdateEvent<T extends BaseObjectMetadata> {
|
export class ObjectRecordUpdateEvent<T> extends ObjectRecordBaseEvent {
|
||||||
workspaceId: string;
|
details: {
|
||||||
previousRecord: T;
|
before: T;
|
||||||
updatedRecord: T;
|
after: T;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { ObjectMetadataInterface } from 'src/engine-metadata/field-metadata/interfaces/object-metadata.interface';
|
||||||
|
|
||||||
|
export class ObjectRecordBaseEvent {
|
||||||
|
workspaceId: string;
|
||||||
|
recordId: string;
|
||||||
|
objectMetadata: ObjectMetadataInterface;
|
||||||
|
details: any;
|
||||||
|
}
|
||||||
@ -44,6 +44,7 @@ import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repos
|
|||||||
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
|
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
|
||||||
import { MessageParticipantObjectMetadata } from 'src/modules/messaging/standard-objects/message-participant.object-metadata';
|
import { MessageParticipantObjectMetadata } from 'src/modules/messaging/standard-objects/message-participant.object-metadata';
|
||||||
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
|
import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-objects/message-channel.object-metadata';
|
||||||
|
import { SaveEventToDbJob } from 'src/engine/api/graphql/workspace-query-runner/jobs/save-event-to-db.job';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -130,6 +131,10 @@ import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-obj
|
|||||||
provide: RecordPositionBackfillJob.name,
|
provide: RecordPositionBackfillJob.name,
|
||||||
useClass: RecordPositionBackfillJob,
|
useClass: RecordPositionBackfillJob,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: SaveEventToDbJob.name,
|
||||||
|
useClass: SaveEventToDbJob,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class JobsModule {
|
export class JobsModule {
|
||||||
|
|||||||
@ -9,4 +9,5 @@ export enum MessageQueue {
|
|||||||
calendarQueue = 'calendar-queue',
|
calendarQueue = 'calendar-queue',
|
||||||
billingQueue = 'billing-queue',
|
billingQueue = 'billing-queue',
|
||||||
recordPositionBackfillQueue = 'record-position-backfill-queue',
|
recordPositionBackfillQueue = 'record-position-backfill-queue',
|
||||||
|
entityEventsToDbQueue = 'entity-events-to-db-queue',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,14 +20,14 @@ export class AnalyticsResolver {
|
|||||||
constructor(private readonly analyticsService: AnalyticsService) {}
|
constructor(private readonly analyticsService: AnalyticsService) {}
|
||||||
|
|
||||||
@Mutation(() => Analytics)
|
@Mutation(() => Analytics)
|
||||||
createEvent(
|
track(
|
||||||
@Args() createEventInput: CreateAnalyticsInput,
|
@Args() createAnalyticsInput: CreateAnalyticsInput,
|
||||||
@AuthWorkspace() workspace: Workspace | undefined,
|
@AuthWorkspace() workspace: Workspace | undefined,
|
||||||
@AuthUser({ allowUndefined: true }) user: User | undefined,
|
@AuthUser({ allowUndefined: true }) user: User | undefined,
|
||||||
@Context('req') request: Request,
|
@Context('req') request: Request,
|
||||||
) {
|
) {
|
||||||
return this.analyticsService.create(
|
return this.analyticsService.create(
|
||||||
createEventInput,
|
createAnalyticsInput,
|
||||||
user,
|
user,
|
||||||
workspace,
|
workspace,
|
||||||
request,
|
request,
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { Workspace } from 'src/engine/modules/workspace/workspace.entity';
|
|||||||
export enum FeatureFlagKeys {
|
export enum FeatureFlagKeys {
|
||||||
IsBlocklistEnabled = 'IS_BLOCKLIST_ENABLED',
|
IsBlocklistEnabled = 'IS_BLOCKLIST_ENABLED',
|
||||||
IsCalendarEnabled = 'IS_CALENDAR_ENABLED',
|
IsCalendarEnabled = 'IS_CALENDAR_ENABLED',
|
||||||
|
IsEventObjectEnabled = 'IS_EVENT_OBJECT_ENABLED',
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity({ name: 'featureFlag', schema: 'core' })
|
@Entity({ name: 'featureFlag', schema: 'core' })
|
||||||
|
|||||||
@ -69,6 +69,9 @@ const getSchemaComponentsProperties = (
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
case FieldMetadataType.JSON:
|
||||||
|
type: 'object';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
itemProperty.type = 'string';
|
itemProperty.type = 'string';
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -62,8 +62,10 @@ export class UserWorkspaceService extends TypeOrmQueryService<UserWorkspace> {
|
|||||||
new ObjectRecordCreateEvent<WorkspaceMemberObjectMetadata>();
|
new ObjectRecordCreateEvent<WorkspaceMemberObjectMetadata>();
|
||||||
|
|
||||||
payload.workspaceId = workspaceId;
|
payload.workspaceId = workspaceId;
|
||||||
payload.createdRecord = new WorkspaceMemberObjectMetadata();
|
payload.details = {
|
||||||
payload.createdRecord = workspaceMember[0];
|
after: workspaceMember[0],
|
||||||
|
};
|
||||||
|
payload.recordId = workspaceMember[0].id;
|
||||||
|
|
||||||
this.eventEmitter.emit('workspaceMember.created', payload);
|
this.eventEmitter.emit('workspaceMember.created', payload);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,8 @@ export const mapFieldMetadataTypeToDataType = (
|
|||||||
return 'boolean';
|
return 'boolean';
|
||||||
case FieldMetadataType.DATE_TIME:
|
case FieldMetadataType.DATE_TIME:
|
||||||
return 'timestamp';
|
return 'timestamp';
|
||||||
|
case FieldMetadataType.JSON:
|
||||||
|
return 'jsonb';
|
||||||
case FieldMetadataType.RATING:
|
case FieldMetadataType.RATING:
|
||||||
case FieldMetadataType.SELECT:
|
case FieldMetadataType.SELECT:
|
||||||
case FieldMetadataType.MULTI_SELECT:
|
case FieldMetadataType.MULTI_SELECT:
|
||||||
|
|||||||
@ -50,6 +50,7 @@ export class AddStandardIdCommand extends CommandRunner {
|
|||||||
{
|
{
|
||||||
IS_BLOCKLIST_ENABLED: true,
|
IS_BLOCKLIST_ENABLED: true,
|
||||||
IS_CALENDAR_ENABLED: true,
|
IS_CALENDAR_ENABLED: true,
|
||||||
|
IS_EVENT_OBJECT_ENABLED: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const standardFieldMetadataCollection = this.standardFieldFactory.create(
|
const standardFieldMetadataCollection = this.standardFieldFactory.create(
|
||||||
@ -61,6 +62,7 @@ export class AddStandardIdCommand extends CommandRunner {
|
|||||||
{
|
{
|
||||||
IS_BLOCKLIST_ENABLED: true,
|
IS_BLOCKLIST_ENABLED: true,
|
||||||
IS_CALENDAR_ENABLED: true,
|
IS_CALENDAR_ENABLED: true,
|
||||||
|
IS_EVENT_OBJECT_ENABLED: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -122,6 +122,7 @@ export const companyStandardFieldIds = {
|
|||||||
opportunities: '20202020-add3-4658-8e23-d70dccb6d0ec',
|
opportunities: '20202020-add3-4658-8e23-d70dccb6d0ec',
|
||||||
favorites: '20202020-4d1d-41ac-b13b-621631298d55',
|
favorites: '20202020-4d1d-41ac-b13b-621631298d55',
|
||||||
attachments: '20202020-c1b5-4120-b0f0-987ca401ed53',
|
attachments: '20202020-c1b5-4120-b0f0-987ca401ed53',
|
||||||
|
events: '20202020-0414-4daf-9c0d-64fe7b27f89f',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const connectedAccountStandardFieldIds = {
|
export const connectedAccountStandardFieldIds = {
|
||||||
@ -135,6 +136,15 @@ export const connectedAccountStandardFieldIds = {
|
|||||||
calendarChannels: '20202020-af4a-47bb-99ec-51911c1d3977',
|
calendarChannels: '20202020-af4a-47bb-99ec-51911c1d3977',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const eventStandardFieldIds = {
|
||||||
|
properties: '20202020-f142-4b04-b91b-6a2b4af3bf10',
|
||||||
|
workspaceMember: '20202020-af23-4479-9a30-868edc474b35',
|
||||||
|
person: '20202020-c414-45b9-a60a-ac27aa96229e',
|
||||||
|
company: '20202020-04ad-4221-a744-7a8278a5ce20',
|
||||||
|
opportunity: '20202020-7664-4a35-a3df-580d389fd5f0',
|
||||||
|
custom: '20202020-4a71-41b0-9f83-9cdcca3f8b14',
|
||||||
|
};
|
||||||
|
|
||||||
export const favoriteStandardFieldIds = {
|
export const favoriteStandardFieldIds = {
|
||||||
position: '20202020-dd26-42c6-8c3c-2a7598c204f6',
|
position: '20202020-dd26-42c6-8c3c-2a7598c204f6',
|
||||||
workspaceMember: '20202020-ce63-49cb-9676-fdc0c45892cd',
|
workspaceMember: '20202020-ce63-49cb-9676-fdc0c45892cd',
|
||||||
@ -199,6 +209,7 @@ export const opportunityStandardFieldIds = {
|
|||||||
favorites: '20202020-a1c2-4500-aaae-83ba8a0e827a',
|
favorites: '20202020-a1c2-4500-aaae-83ba8a0e827a',
|
||||||
activityTargets: '20202020-220a-42d6-8261-b2102d6eab35',
|
activityTargets: '20202020-220a-42d6-8261-b2102d6eab35',
|
||||||
attachments: '20202020-87c7-4118-83d6-2f4031005209',
|
attachments: '20202020-87c7-4118-83d6-2f4031005209',
|
||||||
|
events: '20202020-30e2-421f-96c7-19c69d1cf631',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const personStandardFieldIds = {
|
export const personStandardFieldIds = {
|
||||||
@ -218,6 +229,7 @@ export const personStandardFieldIds = {
|
|||||||
attachments: '20202020-cd97-451f-87fa-bcb789bdbf3a',
|
attachments: '20202020-cd97-451f-87fa-bcb789bdbf3a',
|
||||||
messageParticipants: '20202020-498e-4c61-8158-fa04f0638334',
|
messageParticipants: '20202020-498e-4c61-8158-fa04f0638334',
|
||||||
calendarEventAttendees: '20202020-52ee-45e9-a702-b64b3753e3a9',
|
calendarEventAttendees: '20202020-52ee-45e9-a702-b64b3753e3a9',
|
||||||
|
events: '20202020-a43e-4873-9c23-e522de906ce5',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const pipelineStepStandardFieldIds = {
|
export const pipelineStepStandardFieldIds = {
|
||||||
@ -284,6 +296,7 @@ export const workspaceMemberStandardFieldIds = {
|
|||||||
messageParticipants: '20202020-8f99-48bc-a5eb-edd33dd54188',
|
messageParticipants: '20202020-8f99-48bc-a5eb-edd33dd54188',
|
||||||
blocklist: '20202020-6cb2-4161-9f29-a4b7f1283859',
|
blocklist: '20202020-6cb2-4161-9f29-a4b7f1283859',
|
||||||
calendarEventAttendees: '20202020-0dbc-4841-9ce1-3e793b5b3512',
|
calendarEventAttendees: '20202020-0dbc-4841-9ce1-3e793b5b3512',
|
||||||
|
events: '20202020-e15b-47b8-94fe-8200e3c66615',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const customObjectStandardFieldIds = {
|
export const customObjectStandardFieldIds = {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ export const standardObjectIds = {
|
|||||||
comment: '20202020-435f-4de9-89b5-97e32233bf5f',
|
comment: '20202020-435f-4de9-89b5-97e32233bf5f',
|
||||||
company: '20202020-b374-4779-a561-80086cb2e17f',
|
company: '20202020-b374-4779-a561-80086cb2e17f',
|
||||||
connectedAccount: '20202020-977e-46b2-890b-c3002ddfd5c5',
|
connectedAccount: '20202020-977e-46b2-890b-c3002ddfd5c5',
|
||||||
|
event: '20202020-6736-4337-b5c4-8b39fae325a5',
|
||||||
favorite: '20202020-ab56-4e05-92a3-e2414a499860',
|
favorite: '20202020-ab56-4e05-92a3-e2414a499860',
|
||||||
messageChannelMessageAssociation: '20202020-ad1e-4127-bccb-d83ae04d2ccb',
|
messageChannelMessageAssociation: '20202020-ad1e-4127-bccb-d83ae04d2ccb',
|
||||||
messageChannel: '20202020-fe8c-40bc-a681-b80b771449b7',
|
messageChannel: '20202020-fe8c-40bc-a681-b80b771449b7',
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import { ViewObjectMetadata } from 'src/modules/view/standard-objects/view.objec
|
|||||||
import { WebhookObjectMetadata } from 'src/modules/webhook/standard-objects/webhook.object-metadata';
|
import { WebhookObjectMetadata } from 'src/modules/webhook/standard-objects/webhook.object-metadata';
|
||||||
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
|
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
|
||||||
import { CalendarChannelEventAssociationObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-channel-event-association.object-metadata';
|
import { CalendarChannelEventAssociationObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-channel-event-association.object-metadata';
|
||||||
|
import { EventObjectMetadata } from 'src/modules/event/standard-objects/event.object-metadata';
|
||||||
|
|
||||||
export const standardObjectMetadataDefinitions = [
|
export const standardObjectMetadataDefinitions = [
|
||||||
ActivityTargetObjectMetadata,
|
ActivityTargetObjectMetadata,
|
||||||
@ -35,6 +36,7 @@ export const standardObjectMetadataDefinitions = [
|
|||||||
CommentObjectMetadata,
|
CommentObjectMetadata,
|
||||||
CompanyObjectMetadata,
|
CompanyObjectMetadata,
|
||||||
ConnectedAccountObjectMetadata,
|
ConnectedAccountObjectMetadata,
|
||||||
|
EventObjectMetadata,
|
||||||
FavoriteObjectMetadata,
|
FavoriteObjectMetadata,
|
||||||
OpportunityObjectMetadata,
|
OpportunityObjectMetadata,
|
||||||
PersonObjectMetadata,
|
PersonObjectMetadata,
|
||||||
|
|||||||
@ -19,6 +19,8 @@ import { FavoriteObjectMetadata } from 'src/modules/favorite/standard-objects/fa
|
|||||||
import { OpportunityObjectMetadata } from 'src/modules/opportunity/standard-objects/opportunity.object-metadata';
|
import { OpportunityObjectMetadata } from 'src/modules/opportunity/standard-objects/opportunity.object-metadata';
|
||||||
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
|
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
|
||||||
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
|
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
|
||||||
|
import { EventObjectMetadata } from 'src/modules/event/standard-objects/event.object-metadata';
|
||||||
|
import { Gate } from 'src/engine/workspace-manager/workspace-sync-metadata/decorators/gate.decorator';
|
||||||
|
|
||||||
@ObjectMetadata({
|
@ObjectMetadata({
|
||||||
standardId: standardObjectIds.company,
|
standardId: standardObjectIds.company,
|
||||||
@ -206,4 +208,23 @@ export class CompanyObjectMetadata extends BaseObjectMetadata {
|
|||||||
})
|
})
|
||||||
@IsNullable()
|
@IsNullable()
|
||||||
attachments: AttachmentObjectMetadata[];
|
attachments: AttachmentObjectMetadata[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
standardId: companyStandardFieldIds.events,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Events',
|
||||||
|
description: 'Events linked to the company',
|
||||||
|
icon: 'IconIconTimelineEvent',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
inverseSideTarget: () => EventObjectMetadata,
|
||||||
|
onDelete: RelationOnDeleteAction.CASCADE,
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
@Gate({
|
||||||
|
featureFlag: 'IS_EVENT_OBJECT_ENABLED',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
events: EventObjectMetadata[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,103 @@
|
|||||||
|
import { FieldMetadataType } from 'src/engine-metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { FeatureFlagKeys } from 'src/engine/modules/feature-flag/feature-flag.entity';
|
||||||
|
import { eventStandardFieldIds } 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 { CustomObjectMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/custom-objects/custom.object-metadata';
|
||||||
|
import { DynamicRelationFieldMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/decorators/dynamic-field-metadata.interface';
|
||||||
|
import { FieldMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||||
|
import { Gate } from 'src/engine/workspace-manager/workspace-sync-metadata/decorators/gate.decorator';
|
||||||
|
import { IsNullable } from 'src/engine/workspace-manager/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||||
|
import { IsSystem } from 'src/engine/workspace-manager/workspace-sync-metadata/decorators/is-system.decorator';
|
||||||
|
import { ObjectMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
import { CompanyObjectMetadata } from 'src/modules/company/standard-objects/company.object-metadata';
|
||||||
|
import { OpportunityObjectMetadata } from 'src/modules/opportunity/standard-objects/opportunity.object-metadata';
|
||||||
|
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
|
||||||
|
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
standardId: standardObjectIds.event,
|
||||||
|
namePlural: 'events',
|
||||||
|
labelSingular: 'Event',
|
||||||
|
labelPlural: 'Events',
|
||||||
|
description: 'An event',
|
||||||
|
icon: 'IconJson',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
@Gate({
|
||||||
|
featureFlag: FeatureFlagKeys.IsEventObjectEnabled,
|
||||||
|
})
|
||||||
|
export class EventObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
standardId: eventStandardFieldIds.properties,
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Event name',
|
||||||
|
description: 'Event name/type',
|
||||||
|
icon: 'IconAbc',
|
||||||
|
})
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
standardId: eventStandardFieldIds.properties,
|
||||||
|
type: FieldMetadataType.JSON,
|
||||||
|
label: 'Event details',
|
||||||
|
description: 'Json value for event details',
|
||||||
|
icon: 'IconListDetails',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
properties: JSON;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
standardId: eventStandardFieldIds.workspaceMember,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Workspace Member',
|
||||||
|
description: 'Event workspace member',
|
||||||
|
icon: 'IconCircleUser',
|
||||||
|
joinColumn: 'workspaceMemberId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
workspaceMember: WorkspaceMemberObjectMetadata;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
standardId: eventStandardFieldIds.person,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Person',
|
||||||
|
description: 'Event person',
|
||||||
|
icon: 'IconUser',
|
||||||
|
joinColumn: 'personId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
person: PersonObjectMetadata;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
standardId: eventStandardFieldIds.company,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Company',
|
||||||
|
description: 'Event company',
|
||||||
|
icon: 'IconBuildingSkyscraper',
|
||||||
|
joinColumn: 'companyId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
company: CompanyObjectMetadata;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
standardId: eventStandardFieldIds.opportunity,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Opportunity',
|
||||||
|
description: 'Events opportunity',
|
||||||
|
icon: 'IconTargetArrow',
|
||||||
|
joinColumn: 'opportunityId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
opportunity: OpportunityObjectMetadata;
|
||||||
|
|
||||||
|
@DynamicRelationFieldMetadata((oppositeObjectMetadata) => ({
|
||||||
|
standardId: eventStandardFieldIds.custom,
|
||||||
|
name: oppositeObjectMetadata.nameSingular,
|
||||||
|
label: oppositeObjectMetadata.labelSingular,
|
||||||
|
description: `Favorite ${oppositeObjectMetadata.labelSingular}`,
|
||||||
|
joinColumn: `${oppositeObjectMetadata.nameSingular}Id`,
|
||||||
|
icon: 'IconBuildingSkyscraper',
|
||||||
|
}))
|
||||||
|
custom: CustomObjectMetadata;
|
||||||
|
}
|
||||||
@ -46,7 +46,7 @@ export class MessagingConnectedAccountListener {
|
|||||||
DeleteConnectedAccountAssociatedMessagingDataJob.name,
|
DeleteConnectedAccountAssociatedMessagingDataJob.name,
|
||||||
{
|
{
|
||||||
workspaceId: payload.workspaceId,
|
workspaceId: payload.workspaceId,
|
||||||
connectedAccountId: payload.deletedRecord.id,
|
connectedAccountId: payload.recordId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ export class MessagingConnectedAccountListener {
|
|||||||
DeleteConnectedAccountAssociatedCalendarDataJob.name,
|
DeleteConnectedAccountAssociatedCalendarDataJob.name,
|
||||||
{
|
{
|
||||||
workspaceId: payload.workspaceId,
|
workspaceId: payload.workspaceId,
|
||||||
connectedAccountId: payload.deletedRecord.id,
|
connectedAccountId: payload.recordId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,16 +24,16 @@ export class MessagingMessageChannelListener {
|
|||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
objectRecordChangedProperties(
|
objectRecordChangedProperties(
|
||||||
payload.previousRecord,
|
payload.details.before,
|
||||||
payload.updatedRecord,
|
payload.details.after,
|
||||||
).includes('isContactAutoCreationEnabled') &&
|
).includes('isContactAutoCreationEnabled') &&
|
||||||
payload.updatedRecord.isContactAutoCreationEnabled
|
payload.details.after.isContactAutoCreationEnabled
|
||||||
) {
|
) {
|
||||||
this.messageQueueService.add<CreateCompaniesAndContactsAfterSyncJobData>(
|
this.messageQueueService.add<CreateCompaniesAndContactsAfterSyncJobData>(
|
||||||
CreateCompaniesAndContactsAfterSyncJob.name,
|
CreateCompaniesAndContactsAfterSyncJob.name,
|
||||||
{
|
{
|
||||||
workspaceId: payload.workspaceId,
|
workspaceId: payload.workspaceId,
|
||||||
messageChannelId: payload.updatedRecord.id,
|
messageChannelId: payload.recordId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export class MessagingPersonListener {
|
|||||||
async handleCreatedEvent(
|
async handleCreatedEvent(
|
||||||
payload: ObjectRecordCreateEvent<PersonObjectMetadata>,
|
payload: ObjectRecordCreateEvent<PersonObjectMetadata>,
|
||||||
) {
|
) {
|
||||||
if (payload.createdRecord.email === null) {
|
if (payload.details.after.email === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,8 +31,8 @@ export class MessagingPersonListener {
|
|||||||
MatchMessageParticipantJob.name,
|
MatchMessageParticipantJob.name,
|
||||||
{
|
{
|
||||||
workspaceId: payload.workspaceId,
|
workspaceId: payload.workspaceId,
|
||||||
email: payload.createdRecord.email,
|
email: payload.details.after.email,
|
||||||
personId: payload.createdRecord.id,
|
personId: payload.recordId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -43,16 +43,16 @@ export class MessagingPersonListener {
|
|||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
objectRecordUpdateEventChangedProperties(
|
objectRecordUpdateEventChangedProperties(
|
||||||
payload.previousRecord,
|
payload.details.before,
|
||||||
payload.updatedRecord,
|
payload.details.after,
|
||||||
).includes('email')
|
).includes('email')
|
||||||
) {
|
) {
|
||||||
this.messageQueueService.add<MatchMessageParticipantsJobData>(
|
this.messageQueueService.add<MatchMessageParticipantsJobData>(
|
||||||
MatchMessageParticipantJob.name,
|
MatchMessageParticipantJob.name,
|
||||||
{
|
{
|
||||||
workspaceId: payload.workspaceId,
|
workspaceId: payload.workspaceId,
|
||||||
email: payload.updatedRecord.email,
|
email: payload.details.after.email,
|
||||||
personId: payload.updatedRecord.id,
|
personId: payload.recordId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export class MessagingWorkspaceMemberListener {
|
|||||||
async handleCreatedEvent(
|
async handleCreatedEvent(
|
||||||
payload: ObjectRecordCreateEvent<WorkspaceMemberObjectMetadata>,
|
payload: ObjectRecordCreateEvent<WorkspaceMemberObjectMetadata>,
|
||||||
) {
|
) {
|
||||||
if (payload.createdRecord.userEmail === null) {
|
if (payload.details.after.userEmail === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,8 +31,8 @@ export class MessagingWorkspaceMemberListener {
|
|||||||
MatchMessageParticipantJob.name,
|
MatchMessageParticipantJob.name,
|
||||||
{
|
{
|
||||||
workspaceId: payload.workspaceId,
|
workspaceId: payload.workspaceId,
|
||||||
email: payload.createdRecord.userEmail,
|
email: payload.details.after.userEmail,
|
||||||
workspaceMemberId: payload.createdRecord.id,
|
workspaceMemberId: payload.details.after.id,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -43,16 +43,16 @@ export class MessagingWorkspaceMemberListener {
|
|||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
objectRecordUpdateEventChangedProperties(
|
objectRecordUpdateEventChangedProperties(
|
||||||
payload.previousRecord,
|
payload.details.before,
|
||||||
payload.updatedRecord,
|
payload.details.after,
|
||||||
).includes('userEmail')
|
).includes('userEmail')
|
||||||
) {
|
) {
|
||||||
await this.messageQueueService.add<MatchMessageParticipantsJobData>(
|
await this.messageQueueService.add<MatchMessageParticipantsJobData>(
|
||||||
MatchMessageParticipantJob.name,
|
MatchMessageParticipantJob.name,
|
||||||
{
|
{
|
||||||
workspaceId: payload.workspaceId,
|
workspaceId: payload.workspaceId,
|
||||||
email: payload.updatedRecord.userEmail,
|
email: payload.details.after.userEmail,
|
||||||
workspaceMemberId: payload.updatedRecord.id,
|
workspaceMemberId: payload.recordId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,8 @@ import { CompanyObjectMetadata } from 'src/modules/company/standard-objects/comp
|
|||||||
import { FavoriteObjectMetadata } from 'src/modules/favorite/standard-objects/favorite.object-metadata';
|
import { FavoriteObjectMetadata } from 'src/modules/favorite/standard-objects/favorite.object-metadata';
|
||||||
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
|
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
|
||||||
import { PipelineStepObjectMetadata } from 'src/modules/pipeline-step/standard-objects/pipeline-step.object-metadata';
|
import { PipelineStepObjectMetadata } from 'src/modules/pipeline-step/standard-objects/pipeline-step.object-metadata';
|
||||||
|
import { EventObjectMetadata } from 'src/modules/event/standard-objects/event.object-metadata';
|
||||||
|
import { Gate } from 'src/engine/workspace-manager/workspace-sync-metadata/decorators/gate.decorator';
|
||||||
|
|
||||||
@ObjectMetadata({
|
@ObjectMetadata({
|
||||||
standardId: standardObjectIds.opportunity,
|
standardId: standardObjectIds.opportunity,
|
||||||
@ -178,4 +180,21 @@ export class OpportunityObjectMetadata extends BaseObjectMetadata {
|
|||||||
})
|
})
|
||||||
@IsNullable()
|
@IsNullable()
|
||||||
attachments: AttachmentObjectMetadata[];
|
attachments: AttachmentObjectMetadata[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
standardId: opportunityStandardFieldIds.events,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Events',
|
||||||
|
description: 'Events linked to the opportunity.',
|
||||||
|
icon: 'IconTimelineEvent',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
inverseSideTarget: () => EventObjectMetadata,
|
||||||
|
})
|
||||||
|
@Gate({
|
||||||
|
featureFlag: 'IS_EVENT_OBJECT_ENABLED',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
events: EventObjectMetadata[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import { CompanyObjectMetadata } from 'src/modules/company/standard-objects/comp
|
|||||||
import { FavoriteObjectMetadata } from 'src/modules/favorite/standard-objects/favorite.object-metadata';
|
import { FavoriteObjectMetadata } from 'src/modules/favorite/standard-objects/favorite.object-metadata';
|
||||||
import { MessageParticipantObjectMetadata } from 'src/modules/messaging/standard-objects/message-participant.object-metadata';
|
import { MessageParticipantObjectMetadata } from 'src/modules/messaging/standard-objects/message-participant.object-metadata';
|
||||||
import { OpportunityObjectMetadata } from 'src/modules/opportunity/standard-objects/opportunity.object-metadata';
|
import { OpportunityObjectMetadata } from 'src/modules/opportunity/standard-objects/opportunity.object-metadata';
|
||||||
|
import { EventObjectMetadata } from 'src/modules/event/standard-objects/event.object-metadata';
|
||||||
|
|
||||||
@ObjectMetadata({
|
@ObjectMetadata({
|
||||||
standardId: standardObjectIds.person,
|
standardId: standardObjectIds.person,
|
||||||
@ -218,4 +219,23 @@ export class PersonObjectMetadata extends BaseObjectMetadata {
|
|||||||
})
|
})
|
||||||
@IsSystem()
|
@IsSystem()
|
||||||
calendarEventAttendees: CalendarEventAttendeeObjectMetadata[];
|
calendarEventAttendees: CalendarEventAttendeeObjectMetadata[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
standardId: personStandardFieldIds.events,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Events',
|
||||||
|
description: 'Events linked to the company',
|
||||||
|
icon: 'IconTimelineEvent',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
inverseSideTarget: () => EventObjectMetadata,
|
||||||
|
onDelete: RelationOnDeleteAction.CASCADE,
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
@Gate({
|
||||||
|
featureFlag: 'IS_EVENT_OBJECT_ENABLED',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
events: EventObjectMetadata[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,8 @@ import { CompanyObjectMetadata } from 'src/modules/company/standard-objects/comp
|
|||||||
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
|
import { ConnectedAccountObjectMetadata } from 'src/modules/connected-account/standard-objects/connected-account.object-metadata';
|
||||||
import { FavoriteObjectMetadata } from 'src/modules/favorite/standard-objects/favorite.object-metadata';
|
import { FavoriteObjectMetadata } from 'src/modules/favorite/standard-objects/favorite.object-metadata';
|
||||||
import { MessageParticipantObjectMetadata } from 'src/modules/messaging/standard-objects/message-participant.object-metadata';
|
import { MessageParticipantObjectMetadata } from 'src/modules/messaging/standard-objects/message-participant.object-metadata';
|
||||||
|
import { IsNullable } from 'src/engine/workspace-manager/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||||
|
import { EventObjectMetadata } from 'src/modules/event/standard-objects/event.object-metadata';
|
||||||
|
|
||||||
@ObjectMetadata({
|
@ObjectMetadata({
|
||||||
standardId: standardObjectIds.workspaceMember,
|
standardId: standardObjectIds.workspaceMember,
|
||||||
@ -231,4 +233,23 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
|
|||||||
featureFlag: 'IS_CALENDAR_ENABLED',
|
featureFlag: 'IS_CALENDAR_ENABLED',
|
||||||
})
|
})
|
||||||
calendarEventAttendees: CalendarEventAttendeeObjectMetadata[];
|
calendarEventAttendees: CalendarEventAttendeeObjectMetadata[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
standardId: workspaceMemberStandardFieldIds.events,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Events',
|
||||||
|
description: 'Events linked to the workspace member',
|
||||||
|
icon: 'IconTimelineEvent',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
inverseSideTarget: () => EventObjectMetadata,
|
||||||
|
onDelete: RelationOnDeleteAction.CASCADE,
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
@Gate({
|
||||||
|
featureFlag: 'IS_EVENT_OBJECT_ENABLED',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
events: EventObjectMetadata[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user