Separate system operations from core objects in GraphQL endpoints (#12977)

Moves system-level operations (auth, billing, admin) to use the
/metadata endpoint instead of /graphql.

This cleans up the endpoint separation so /graphql is purely for core
objects (Company, People, etc.) and /metadata handles all system
operations.

Part of prep work for webhook/API key core migration.
This commit is contained in:
nitin
2025-07-01 21:59:32 +05:30
committed by GitHub
parent 76c517aa29
commit d2ddd6f473
229 changed files with 9425 additions and 8804 deletions

View File

@ -0,0 +1,21 @@
import { useApolloFactory } from '@/apollo/hooks/useApolloFactory';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { ApolloCoreClientContext } from '../contexts/ApolloCoreClientContext';
export const ApolloCoreProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const apolloCoreClient = useApolloFactory({
uri: `${REACT_APP_SERVER_BASE_URL}/graphql`,
connectToDevTools: true, // @Felix I am not sure if this is correct, should be false?
});
return (
<ApolloCoreClientContext.Provider value={apolloCoreClient}>
{children}
</ApolloCoreClientContext.Provider>
);
};

View File

@ -1,21 +0,0 @@
import { useApolloFactory } from '@/apollo/hooks/useApolloFactory';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { ApolloMetadataClientContext } from '../contexts/ApolloClientMetadataContext';
export const ApolloMetadataClientProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const apolloMetadataClient = useApolloFactory({
uri: `${REACT_APP_SERVER_BASE_URL}/metadata`,
connectToDevTools: false,
});
return (
<ApolloMetadataClientContext.Provider value={apolloMetadataClient}>
{children}
</ApolloMetadataClientContext.Provider>
);
};

View File

@ -1,5 +1,5 @@
import { createContext } from 'react';
import { ApolloClient, NormalizedCacheObject } from '@apollo/client';
export const ApolloMetadataClientContext =
export const ApolloCoreClientContext =
createContext<ApolloClient<NormalizedCacheObject> | null>(null);

View File

@ -0,0 +1,16 @@
import { ReactNode } from 'react';
import { ApolloCoreClientContext } from '@/object-metadata/contexts/ApolloCoreClientContext';
import { mockedApolloCoreClient } from '~/testing/mockedApolloCoreClient';
export const ApolloCoreClientMockedProvider = ({
children,
}: {
children: ReactNode;
}) => {
return (
<ApolloCoreClientContext.Provider value={mockedApolloCoreClient}>
{mockedApolloCoreClient ? children : ''}
</ApolloCoreClientContext.Provider>
);
};

View File

@ -1,16 +0,0 @@
import { ReactNode } from 'react';
import { ApolloMetadataClientContext } from '@/object-metadata/contexts/ApolloClientMetadataContext';
import { mockedMetadataApolloClient } from '~/testing/mockedMetadataApolloClient';
export const ApolloMetadataClientMockedProvider = ({
children,
}: {
children: ReactNode;
}) => {
return (
<ApolloMetadataClientContext.Provider value={mockedMetadataApolloClient}>
{mockedMetadataApolloClient ? children : ''}
</ApolloMetadataClientContext.Provider>
);
};

View File

@ -3,7 +3,7 @@ import { act } from 'react';
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { FieldMetadataType, RelationType } from '~/generated/graphql';
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
import {

View File

@ -0,0 +1,18 @@
import { ApolloCoreClientContext } from '@/object-metadata/contexts/ApolloCoreClientContext';
import { useApolloClient } from '@apollo/client';
import { useContext } from 'react';
export const useApolloCoreClient = () => {
const apolloCoreClient = useContext(ApolloCoreClientContext);
const apolloClient = useApolloClient();
if (process.env.NODE_ENV === 'test') {
return apolloClient;
}
if (!apolloCoreClient) {
throw new Error('ApolloCoreClient not found');
}
return apolloCoreClient;
};

View File

@ -1,18 +0,0 @@
import { ApolloMetadataClientContext } from '@/object-metadata/contexts/ApolloClientMetadataContext';
import { useApolloClient } from '@apollo/client';
import { useContext } from 'react';
export const useApolloMetadataClient = () => {
const apolloMetadataClient = useContext(ApolloMetadataClientContext);
const apolloClient = useApolloClient();
if (process.env.NODE_ENV === 'test') {
return apolloClient;
}
if (!apolloMetadataClient) {
throw new Error('ApolloMetadataClient not found');
}
return apolloMetadataClient;
};

View File

@ -9,19 +9,15 @@ import {
import { CREATE_ONE_FIELD_METADATA_ITEM } from '../graphql/mutations';
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem';
import { useApolloMetadataClient } from './useApolloMetadataClient';
export const useCreateOneFieldMetadataItem = () => {
const apolloMetadataClient = useApolloMetadataClient();
const { refreshObjectMetadataItems } =
useRefreshObjectMetadataItems('network-only');
const [mutate] = useMutation<
CreateOneFieldMetadataItemMutation,
CreateOneFieldMetadataItemMutationVariables
>(CREATE_ONE_FIELD_METADATA_ITEM, {
client: apolloMetadataClient,
});
>(CREATE_ONE_FIELD_METADATA_ITEM);
const createOneFieldMetadataItem = async (input: CreateFieldInput) => {
const result = await mutate({

View File

@ -10,21 +10,17 @@ import { CREATE_ONE_OBJECT_METADATA_ITEM } from '../graphql/mutations';
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem';
import { useRefreshCachedViews } from '@/views/hooks/useRefreshViews';
import { useApolloMetadataClient } from './useApolloMetadataClient';
export const useCreateOneObjectMetadataItem = () => {
const { refreshCachedViews } = useRefreshCachedViews();
const apolloMetadataClient = useApolloMetadataClient();
const { refreshObjectMetadataItems } =
useRefreshObjectMetadataItems('network-only');
const [mutate] = useMutation<
CreateOneObjectMetadataItemMutation,
CreateOneObjectMetadataItemMutationVariables
>(CREATE_ONE_OBJECT_METADATA_ITEM, {
client: apolloMetadataClient,
});
>(CREATE_ONE_OBJECT_METADATA_ITEM);
const createOneObjectMetadataItem = async (input: CreateObjectInput) => {
const createdObjectMetadata = await mutate({

View File

@ -10,17 +10,12 @@ import { recordIndexKanbanAggregateOperationState } from '@/object-record/record
import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations';
import { useRecoilState } from 'recoil';
import { DELETE_ONE_FIELD_METADATA_ITEM } from '../graphql/mutations';
import { useApolloMetadataClient } from './useApolloMetadataClient';
export const useDeleteOneFieldMetadataItem = () => {
const apolloMetadataClient = useApolloMetadataClient();
const [mutate] = useMutation<
DeleteOneFieldMetadataItemMutation,
DeleteOneFieldMetadataItemMutationVariables
>(DELETE_ONE_FIELD_METADATA_ITEM, {
client: apolloMetadataClient,
});
>(DELETE_ONE_FIELD_METADATA_ITEM);
const { refreshObjectMetadataItems } =
useRefreshObjectMetadataItems('network-only');

View File

@ -8,17 +8,12 @@ import {
import { DELETE_ONE_OBJECT_METADATA_ITEM } from '../graphql/mutations';
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem';
import { useApolloMetadataClient } from './useApolloMetadataClient';
export const useDeleteOneObjectMetadataItem = () => {
const apolloMetadataClient = useApolloMetadataClient();
const [mutate] = useMutation<
DeleteOneObjectMetadataItemMutation,
DeleteOneObjectMetadataItemMutationVariables
>(DELETE_ONE_OBJECT_METADATA_ITEM, {
client: apolloMetadataClient,
});
>(DELETE_ONE_OBJECT_METADATA_ITEM);
const { refreshObjectMetadataItems } =
useRefreshObjectMetadataItems('network-only');

View File

@ -12,23 +12,18 @@ import { logError } from '~/utils/logError';
import { FIND_MANY_OBJECT_METADATA_ITEMS } from '../graphql/queries';
import { mapPaginatedObjectMetadataItemsToObjectMetadataItems } from '../utils/mapPaginatedObjectMetadataItemsToObjectMetadataItems';
import { useApolloMetadataClient } from './useApolloMetadataClient';
export const useFindManyObjectMetadataItems = ({
skip,
}: {
skip?: boolean;
} = {}) => {
const apolloMetadataClient = useApolloMetadataClient();
const { enqueueSnackBar } = useSnackBar();
const { data, loading, error, refetch } = useQuery<
ObjectMetadataItemsQuery,
ObjectMetadataItemsQueryVariables
>(FIND_MANY_OBJECT_METADATA_ITEMS, {
client: apolloMetadataClient ?? undefined,
skip: skip || !apolloMetadataClient,
skip,
onError: (error) => {
logError('useFindManyObjectMetadataItems error : ' + error);
enqueueSnackBar(`${error.message}`, {

View File

@ -1,10 +1,9 @@
import { FIND_MANY_OBJECT_METADATA_ITEMS } from '@/object-metadata/graphql/queries';
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { mapPaginatedObjectMetadataItemsToObjectMetadataItems } from '@/object-metadata/utils/mapPaginatedObjectMetadataItemsToObjectMetadataItems';
import { FetchPolicy } from '@apollo/client';
import { FetchPolicy, useApolloClient } from '@apollo/client';
import { useRecoilCallback } from 'recoil';
import { ObjectMetadataItemsQuery } from '~/generated-metadata/graphql';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
@ -12,7 +11,7 @@ import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
export const useRefreshObjectMetadataItems = (
fetchPolicy: FetchPolicy = 'network-only',
) => {
const client = useApolloMetadataClient();
const client = useApolloClient();
const refreshObjectMetadataItems = async () => {
const result = await client.query<ObjectMetadataItemsQuery>({

View File

@ -18,10 +18,8 @@ import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getR
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { useSetRecordGroups } from '@/object-record/record-group/hooks/useSetRecordGroups';
import { isDefined } from 'twenty-shared/utils';
import { useApolloMetadataClient } from './useApolloMetadataClient';
export const useUpdateOneFieldMetadataItem = () => {
const apolloMetadataClient = useApolloMetadataClient();
const apolloClient = useApolloClient();
const { refreshObjectMetadataItems } =
useRefreshObjectMetadataItems('network-only');
@ -48,9 +46,7 @@ export const useUpdateOneFieldMetadataItem = () => {
const [mutate] = useMutation<
UpdateOneFieldMetadataItemMutation,
UpdateOneFieldMetadataItemMutationVariables
>(UPDATE_ONE_FIELD_METADATA_ITEM, {
client: apolloMetadataClient ?? undefined,
});
>(UPDATE_ONE_FIELD_METADATA_ITEM);
const updateOneFieldMetadataItem = async ({
objectMetadataId,

View File

@ -9,18 +9,13 @@ import {
import { UPDATE_ONE_OBJECT_METADATA_ITEM } from '../graphql/mutations';
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem';
import { useApolloMetadataClient } from './useApolloMetadataClient';
// TODO: Slice the Apollo store synchronously in the update function instead of subscribing, so we can use update after read in the same function call
export const useUpdateOneObjectMetadataItem = () => {
const apolloClientMetadata = useApolloMetadataClient();
const [mutate, { loading }] = useMutation<
UpdateOneObjectMetadataItemMutation,
UpdateOneObjectMetadataItemMutationVariables
>(UPDATE_ONE_OBJECT_METADATA_ITEM, {
client: apolloClientMetadata ?? undefined,
});
>(UPDATE_ONE_OBJECT_METADATA_ITEM);
const { refreshObjectMetadataItems } =
useRefreshObjectMetadataItems('network-only');