Fix ObjectType casing and conflict between Relation and RelationMetadata (#9849)

Fixes #9827 

Also uncovered a conflict with `@objectType('Relation')` and
`@objectType('relation)`

I don't want to address it in this PR so I will create a followup issue
when we close this but I think there's a confusion between
Relation/RelationMetadata, it's unclear what is what

---------

Co-authored-by: Antoine Moreaux <moreaux.antoine@gmail.com>
This commit is contained in:
Félix Malfait
2025-01-28 10:06:18 +01:00
committed by GitHub
parent 08a0e67477
commit af8d22ee99
70 changed files with 22258 additions and 22058 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
import * as Apollo from '@apollo/client';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
@ -401,6 +401,32 @@ export enum FeatureFlagKey {
IsWorkflowEnabled = 'IsWorkflowEnabled'
}
export type Field = {
__typename?: 'Field';
createdAt: Scalars['DateTime'];
defaultValue?: Maybe<Scalars['JSON']>;
description?: Maybe<Scalars['String']>;
fromRelationMetadata?: Maybe<RelationMetadata>;
icon?: Maybe<Scalars['String']>;
id: Scalars['UUID'];
isActive?: Maybe<Scalars['Boolean']>;
isCustom?: Maybe<Scalars['Boolean']>;
isLabelSyncedWithName?: Maybe<Scalars['Boolean']>;
isNullable?: Maybe<Scalars['Boolean']>;
isSystem?: Maybe<Scalars['Boolean']>;
isUnique?: Maybe<Scalars['Boolean']>;
label: Scalars['String'];
name: Scalars['String'];
object?: Maybe<Object>;
options?: Maybe<Scalars['JSON']>;
relation?: Maybe<Relation>;
relationDefinition?: Maybe<RelationDefinition>;
settings?: Maybe<Scalars['JSON']>;
toRelationMetadata?: Maybe<RelationMetadata>;
type: FieldMetadataType;
updatedAt: Scalars['DateTime'];
};
export type FieldConnection = {
__typename?: 'FieldConnection';
/** Array of edges. */
@ -409,6 +435,23 @@ export type FieldConnection = {
pageInfo: PageInfo;
};
export type FieldEdge = {
__typename?: 'FieldEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor'];
/** The node containing the Field */
node: Field;
};
export type FieldFilter = {
and?: InputMaybe<Array<FieldFilter>>;
id?: InputMaybe<UuidFilterComparison>;
isActive?: InputMaybe<BooleanFieldComparison>;
isCustom?: InputMaybe<BooleanFieldComparison>;
isSystem?: InputMaybe<BooleanFieldComparison>;
or?: InputMaybe<Array<FieldFilter>>;
};
/** Type of the field */
export enum FieldMetadataType {
ACTOR = 'ACTOR',
@ -489,6 +532,32 @@ export type ImpersonateOutput = {
workspace: WorkspaceSubdomainAndId;
};
export type Index = {
__typename?: 'Index';
createdAt: Scalars['DateTime'];
id: Scalars['UUID'];
indexFieldMetadatas: IndexIndexFieldMetadatasConnection;
indexType: IndexType;
indexWhereClause?: Maybe<Scalars['String']>;
isCustom?: Maybe<Scalars['Boolean']>;
isUnique: Scalars['Boolean'];
name: Scalars['String'];
objectMetadata: IndexObjectMetadataConnection;
updatedAt: Scalars['DateTime'];
};
export type IndexIndexFieldMetadatasArgs = {
filter?: IndexFieldFilter;
paging?: CursorPaging;
};
export type IndexObjectMetadataArgs = {
filter?: ObjectFilter;
paging?: CursorPaging;
};
export type IndexConnection = {
__typename?: 'IndexConnection';
/** Array of edges. */
@ -497,6 +566,45 @@ export type IndexConnection = {
pageInfo: PageInfo;
};
export type IndexEdge = {
__typename?: 'IndexEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor'];
/** The node containing the Index */
node: Index;
};
export type IndexField = {
__typename?: 'IndexField';
createdAt: Scalars['DateTime'];
fieldMetadataId: Scalars['UUID'];
id: Scalars['UUID'];
order: Scalars['Float'];
updatedAt: Scalars['DateTime'];
};
export type IndexFieldEdge = {
__typename?: 'IndexFieldEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor'];
/** The node containing the IndexField */
node: IndexField;
};
export type IndexFieldFilter = {
and?: InputMaybe<Array<IndexFieldFilter>>;
fieldMetadataId?: InputMaybe<UuidFilterComparison>;
id?: InputMaybe<UuidFilterComparison>;
or?: InputMaybe<Array<IndexFieldFilter>>;
};
export type IndexFilter = {
and?: InputMaybe<Array<IndexFilter>>;
id?: InputMaybe<UuidFilterComparison>;
isCustom?: InputMaybe<BooleanFieldComparison>;
or?: InputMaybe<Array<IndexFilter>>;
};
export type IndexIndexFieldMetadatasConnection = {
__typename?: 'IndexIndexFieldMetadatasConnection';
/** Array of edges. */
@ -871,6 +979,42 @@ export type MutationUserLookupAdminPanelArgs = {
userIdentifier: Scalars['String'];
};
export type Object = {
__typename?: 'Object';
createdAt: Scalars['DateTime'];
dataSourceId: Scalars['String'];
description?: Maybe<Scalars['String']>;
fields: ObjectFieldsConnection;
icon?: Maybe<Scalars['String']>;
id: Scalars['UUID'];
imageIdentifierFieldMetadataId?: Maybe<Scalars['String']>;
indexMetadatas: ObjectIndexMetadatasConnection;
isActive: Scalars['Boolean'];
isCustom: Scalars['Boolean'];
isLabelSyncedWithName: Scalars['Boolean'];
isRemote: Scalars['Boolean'];
isSystem: Scalars['Boolean'];
labelIdentifierFieldMetadataId?: Maybe<Scalars['String']>;
labelPlural: Scalars['String'];
labelSingular: Scalars['String'];
namePlural: Scalars['String'];
nameSingular: Scalars['String'];
shortcut?: Maybe<Scalars['String']>;
updatedAt: Scalars['DateTime'];
};
export type ObjectFieldsArgs = {
filter?: FieldFilter;
paging?: CursorPaging;
};
export type ObjectIndexMetadatasArgs = {
filter?: IndexFilter;
paging?: CursorPaging;
};
export type ObjectConnection = {
__typename?: 'ObjectConnection';
/** Array of edges. */
@ -879,6 +1023,14 @@ export type ObjectConnection = {
pageInfo: PageInfo;
};
export type ObjectEdge = {
__typename?: 'ObjectEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor'];
/** The node containing the Object */
node: Object;
};
export type ObjectFieldsConnection = {
__typename?: 'ObjectFieldsConnection';
/** Array of edges. */
@ -887,6 +1039,16 @@ export type ObjectFieldsConnection = {
pageInfo: PageInfo;
};
export type ObjectFilter = {
and?: InputMaybe<Array<ObjectFilter>>;
id?: InputMaybe<UuidFilterComparison>;
isActive?: InputMaybe<BooleanFieldComparison>;
isCustom?: InputMaybe<BooleanFieldComparison>;
isRemote?: InputMaybe<BooleanFieldComparison>;
isSystem?: InputMaybe<BooleanFieldComparison>;
or?: InputMaybe<Array<ObjectFilter>>;
};
export type ObjectIndexMetadatasConnection = {
__typename?: 'ObjectIndexMetadatasConnection';
/** Array of edges. */
@ -1079,14 +1241,6 @@ export type Relation = {
type: RelationType;
};
export type RelationConnection = {
__typename?: 'RelationConnection';
/** Array of edges. */
edges: Array<RelationEdge>;
/** Paging information */
pageInfo: PageInfo;
};
export type RelationDefinition = {
__typename?: 'RelationDefinition';
direction: RelationDefinitionType;
@ -1105,6 +1259,36 @@ export enum RelationDefinitionType {
ONE_TO_ONE = 'ONE_TO_ONE'
}
export type RelationMetadata = {
__typename?: 'RelationMetadata';
createdAt: Scalars['DateTime'];
fromFieldMetadataId: Scalars['String'];
fromObjectMetadata: Object;
fromObjectMetadataId: Scalars['String'];
id: Scalars['UUID'];
relationType: RelationMetadataType;
toFieldMetadataId: Scalars['String'];
toObjectMetadata: Object;
toObjectMetadataId: Scalars['String'];
updatedAt: Scalars['DateTime'];
};
export type RelationMetadataConnection = {
__typename?: 'RelationMetadataConnection';
/** Array of edges. */
edges: Array<RelationMetadataEdge>;
/** Paging information */
pageInfo: PageInfo;
};
export type RelationMetadataEdge = {
__typename?: 'RelationMetadataEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor'];
/** The node containing the RelationMetadata */
node: RelationMetadata;
};
/** Type of the relation */
export enum RelationMetadataType {
MANY_TO_MANY = 'MANY_TO_MANY',
@ -1668,176 +1852,6 @@ export type WorkspaceSubdomainAndId = {
subdomain: Scalars['String'];
};
export type Field = {
__typename?: 'field';
createdAt: Scalars['DateTime'];
defaultValue?: Maybe<Scalars['JSON']>;
description?: Maybe<Scalars['String']>;
fromRelationMetadata?: Maybe<Relation>;
icon?: Maybe<Scalars['String']>;
id: Scalars['UUID'];
isActive?: Maybe<Scalars['Boolean']>;
isCustom?: Maybe<Scalars['Boolean']>;
isLabelSyncedWithName?: Maybe<Scalars['Boolean']>;
isNullable?: Maybe<Scalars['Boolean']>;
isSystem?: Maybe<Scalars['Boolean']>;
isUnique?: Maybe<Scalars['Boolean']>;
label: Scalars['String'];
name: Scalars['String'];
object?: Maybe<Object>;
options?: Maybe<Scalars['JSON']>;
relation?: Maybe<Relation>;
relationDefinition?: Maybe<RelationDefinition>;
settings?: Maybe<Scalars['JSON']>;
toRelationMetadata?: Maybe<Relation>;
type: FieldMetadataType;
updatedAt: Scalars['DateTime'];
};
export type FieldEdge = {
__typename?: 'fieldEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor'];
/** The node containing the field */
node: Field;
};
export type FieldFilter = {
and?: InputMaybe<Array<FieldFilter>>;
id?: InputMaybe<UuidFilterComparison>;
isActive?: InputMaybe<BooleanFieldComparison>;
isCustom?: InputMaybe<BooleanFieldComparison>;
isSystem?: InputMaybe<BooleanFieldComparison>;
or?: InputMaybe<Array<FieldFilter>>;
};
export type Index = {
__typename?: 'index';
createdAt: Scalars['DateTime'];
id: Scalars['UUID'];
indexFieldMetadatas: IndexIndexFieldMetadatasConnection;
indexType: IndexType;
indexWhereClause?: Maybe<Scalars['String']>;
isCustom?: Maybe<Scalars['Boolean']>;
isUnique: Scalars['Boolean'];
name: Scalars['String'];
objectMetadata: IndexObjectMetadataConnection;
updatedAt: Scalars['DateTime'];
};
export type IndexIndexFieldMetadatasArgs = {
filter?: IndexFieldFilter;
paging?: CursorPaging;
};
export type IndexObjectMetadataArgs = {
filter?: ObjectFilter;
paging?: CursorPaging;
};
export type IndexEdge = {
__typename?: 'indexEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor'];
/** The node containing the index */
node: Index;
};
export type IndexField = {
__typename?: 'indexField';
createdAt: Scalars['DateTime'];
fieldMetadataId: Scalars['UUID'];
id: Scalars['UUID'];
order: Scalars['Float'];
updatedAt: Scalars['DateTime'];
};
export type IndexFieldEdge = {
__typename?: 'indexFieldEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor'];
/** The node containing the indexField */
node: IndexField;
};
export type IndexFieldFilter = {
and?: InputMaybe<Array<IndexFieldFilter>>;
fieldMetadataId?: InputMaybe<UuidFilterComparison>;
id?: InputMaybe<UuidFilterComparison>;
or?: InputMaybe<Array<IndexFieldFilter>>;
};
export type IndexFilter = {
and?: InputMaybe<Array<IndexFilter>>;
id?: InputMaybe<UuidFilterComparison>;
isCustom?: InputMaybe<BooleanFieldComparison>;
or?: InputMaybe<Array<IndexFilter>>;
};
export type Object = {
__typename?: 'object';
createdAt: Scalars['DateTime'];
dataSourceId: Scalars['String'];
description?: Maybe<Scalars['String']>;
fields: ObjectFieldsConnection;
icon?: Maybe<Scalars['String']>;
id: Scalars['UUID'];
imageIdentifierFieldMetadataId?: Maybe<Scalars['String']>;
indexMetadatas: ObjectIndexMetadatasConnection;
isActive: Scalars['Boolean'];
isCustom: Scalars['Boolean'];
isLabelSyncedWithName: Scalars['Boolean'];
isRemote: Scalars['Boolean'];
isSystem: Scalars['Boolean'];
labelIdentifierFieldMetadataId?: Maybe<Scalars['String']>;
labelPlural: Scalars['String'];
labelSingular: Scalars['String'];
namePlural: Scalars['String'];
nameSingular: Scalars['String'];
shortcut?: Maybe<Scalars['String']>;
updatedAt: Scalars['DateTime'];
};
export type ObjectFieldsArgs = {
filter?: FieldFilter;
paging?: CursorPaging;
};
export type ObjectIndexMetadatasArgs = {
filter?: IndexFilter;
paging?: CursorPaging;
};
export type ObjectEdge = {
__typename?: 'objectEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor'];
/** The node containing the object */
node: Object;
};
export type ObjectFilter = {
and?: InputMaybe<Array<ObjectFilter>>;
id?: InputMaybe<UuidFilterComparison>;
isActive?: InputMaybe<BooleanFieldComparison>;
isCustom?: InputMaybe<BooleanFieldComparison>;
isRemote?: InputMaybe<BooleanFieldComparison>;
isSystem?: InputMaybe<BooleanFieldComparison>;
or?: InputMaybe<Array<ObjectFilter>>;
};
export type RelationEdge = {
__typename?: 'relationEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor'];
/** The node containing the relation */
node: Relation;
};
export type TimelineCalendarEventFragmentFragment = { __typename?: 'TimelineCalendarEvent', id: any, title: string, description: string, location: string, startsAt: string, endsAt: string, isFullDay: boolean, visibility: CalendarChannelVisibility, participants: Array<{ __typename?: 'TimelineCalendarEventParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> };
export type TimelineCalendarEventParticipantFragmentFragment = { __typename?: 'TimelineCalendarEventParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string };

View File

@ -43,8 +43,10 @@ export const CREATE_ONE_FIELD_METADATA_ITEM = gql`
`;
export const CREATE_ONE_RELATION_METADATA_ITEM = gql`
mutation CreateOneRelationMetadata($input: CreateOneRelationInput!) {
createOneRelation(input: $input) {
mutation CreateOneRelationMetadataItem(
$input: CreateOneRelationMetadataInput!
) {
createOneRelationMetadata(input: $input) {
id
relationType
fromObjectMetadataId

View File

@ -2,8 +2,8 @@ import { gql } from '@apollo/client';
export const FIND_MANY_OBJECT_METADATA_ITEMS = gql`
query ObjectMetadataItems(
$objectFilter: objectFilter
$fieldFilter: fieldFilter
$objectFilter: ObjectFilter
$fieldFilter: FieldFilter
) {
objects(paging: { first: 1000 }, filter: $objectFilter) {
edges {

View File

@ -1,8 +1,10 @@
import { gql } from '@apollo/client';
export const query = gql`
mutation CreateOneRelationMetadata($input: CreateOneRelationInput!) {
createOneRelation(input: $input) {
mutation CreateOneRelationMetadataItem(
$input: CreateOneRelationMetadataInput!
) {
createOneRelationMetadata(input: $input) {
id
relationType
fromObjectMetadataId
@ -17,7 +19,7 @@ export const query = gql`
export const variables = {
input: {
relation: {
relationMetadata: {
fromDescription: null,
fromIcon: undefined,
fromLabel: 'label',

View File

@ -2,7 +2,7 @@ import { gql } from '@apollo/client';
import { mockedStandardObjectMetadataQueryResult } from '~/testing/mock-data/generated/mock-metadata-query-result';
export const query = gql`
query ObjectMetadataItems($objectFilter: objectFilter, $fieldFilter: fieldFilter) {
query ObjectMetadataItems($objectFilter: ObjectFilter, $fieldFilter: FieldFilter) {
objects(paging: {first: 1000}, filter: $objectFilter) {
edges {
node {

View File

@ -1,8 +1,8 @@
import { useMutation } from '@apollo/client';
import {
CreateOneRelationMetadataMutation,
CreateOneRelationMetadataMutationVariables,
CreateOneRelationMetadataItemMutation,
CreateOneRelationMetadataItemMutationVariables,
} from '~/generated-metadata/graphql';
import { CREATE_ONE_RELATION_METADATA_ITEM } from '../graphql/mutations';
@ -18,8 +18,8 @@ export const useCreateOneRelationMetadataItem = () => {
const apolloMetadataClient = useApolloMetadataClient();
const [mutate] = useMutation<
CreateOneRelationMetadataMutation,
CreateOneRelationMetadataMutationVariables
CreateOneRelationMetadataItemMutation,
CreateOneRelationMetadataItemMutationVariables
>(CREATE_ONE_RELATION_METADATA_ITEM, {
client: apolloMetadataClient,
});
@ -31,7 +31,9 @@ export const useCreateOneRelationMetadataItem = () => {
input: FormatRelationMetadataInputParams,
) => {
const result = await mutate({
variables: { input: { relation: formatRelationMetadataInput(input) } },
variables: {
input: { relationMetadata: formatRelationMetadataInput(input) },
},
});
await refreshObjectMetadataItems();

View File

@ -11,7 +11,7 @@ import { camelCaseStringSchema } from '~/utils/validation-schemas/camelCaseStrin
export const fieldMetadataItemSchema = (existingLabels?: string[]) => {
return z.object({
__typename: z.literal('field').optional(),
__typename: z.literal('Field').optional(),
createdAt: z.string().datetime(),
defaultValue: z.any().optional(),
description: z.string().trim().nullable().optional(),
@ -44,23 +44,23 @@ export const fieldMetadataItemSchema = (existingLabels?: string[]) => {
relationId: z.string().uuid(),
direction: z.nativeEnum(RelationDefinitionType),
sourceFieldMetadata: z.object({
__typename: z.literal('field').optional(),
__typename: z.literal('Field').optional(),
id: z.string().uuid(),
name: z.string().trim().min(1),
}),
sourceObjectMetadata: z.object({
__typename: z.literal('object').optional(),
__typename: z.literal('Object').optional(),
id: z.string().uuid(),
namePlural: z.string().trim().min(1),
nameSingular: z.string().trim().min(1),
}),
targetFieldMetadata: z.object({
__typename: z.literal('field').optional(),
__typename: z.literal('Field').optional(),
id: z.string().uuid(),
name: z.string().trim().min(1),
}),
targetObjectMetadata: z.object({
__typename: z.literal('object').optional(),
__typename: z.literal('Object').optional(),
id: z.string().uuid(),
namePlural: z.string().trim().min(1),
nameSingular: z.string().trim().min(1),

View File

@ -3,7 +3,7 @@ import { z } from 'zod';
import { IndexFieldMetadataItem } from '@/object-metadata/types/IndexFieldMetadataItem';
export const indexFieldMetadataItemSchema = z.object({
__typename: z.literal('indexField'),
__typename: z.literal('IndexField'),
fieldMetadataId: z.string().uuid(),
id: z.string(),
createdAt: z.string(),

View File

@ -5,7 +5,7 @@ import { indexFieldMetadataItemSchema } from '@/object-metadata/validation-schem
import { IndexType } from '~/generated-metadata/graphql';
export const indexMetadataItemSchema = z.object({
__typename: z.literal('index'),
__typename: z.literal('Index'),
id: z.string().uuid(),
name: z.string(),
indexFieldMetadatas: z.array(indexFieldMetadataItemSchema),

View File

@ -7,7 +7,7 @@ import { metadataLabelSchema } from '@/object-metadata/validation-schemas/metada
import { camelCaseStringSchema } from '~/utils/validation-schemas/camelCaseStringSchema';
export const objectMetadataItemSchema = z.object({
__typename: z.literal('object').optional(),
__typename: z.literal('Object').optional(),
createdAt: z.string().datetime(),
dataSourceId: z.string().uuid(),
description: z.string().trim().nullable().optional(),

View File

@ -17,7 +17,7 @@ import {
IconSettings,
} from 'twenty-ui';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FeatureFlag, FeatureFlagKey } from '~/generated/graphql';
import { FeatureFlagKey } from '~/generated/graphql';
export const useRecordShowContainerTabs = (
loading: boolean,
@ -252,7 +252,7 @@ export const useRecordShowContainerTabs = (
hide.ifFeaturesDisabled.length > 0 &&
!hide.ifFeaturesDisabled.every((flagKey) => {
return !!currentWorkspace?.featureFlags?.find(
(flag: FeatureFlag) => flag.key === flagKey && flag.value,
(flag) => flag.key === flagKey && flag.value,
);
});