Fix remote object read-only + remove relations (#4921)

- Set `readOnly` boolean in table row context. Preventing updates and
deletion
- Show page is null for remote objects. No need for complicated design
since this is temporary?
- Relation creations are now behind a feature flag for remote objects
- Refetch objects and views after syncing objects

---------

Co-authored-by: Thomas Trompette <thomast@twenty.com>
This commit is contained in:
Thomas Trompette
2024-04-11 17:58:02 +02:00
committed by GitHub
parent fc56775c2a
commit f332213e0d
29 changed files with 275 additions and 158 deletions

View File

@ -1,10 +1,13 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { ApolloClient, useMutation } from '@apollo/client'; import { ApolloClient, useApolloClient, useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities'; import { getOperationName } from '@apollo/client/utilities';
import { SYNC_REMOTE_TABLE } from '@/databases/graphql/mutations/syncRemoteTable'; import { SYNC_REMOTE_TABLE } from '@/databases/graphql/mutations/syncRemoteTable';
import { GET_MANY_REMOTE_TABLES } from '@/databases/graphql/queries/findManyRemoteTables'; import { GET_MANY_REMOTE_TABLES } from '@/databases/graphql/queries/findManyRemoteTables';
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient'; import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
import { import {
RemoteTableInput, RemoteTableInput,
SyncRemoteTableMutation, SyncRemoteTableMutation,
@ -13,6 +16,14 @@ import {
export const useSyncRemoteTable = () => { export const useSyncRemoteTable = () => {
const apolloMetadataClient = useApolloMetadataClient(); const apolloMetadataClient = useApolloMetadataClient();
const apolloClient = useApolloClient();
const { refetch: refetchObjectMetadataItems } =
useFindManyObjectMetadataItems();
const { findManyRecordsQuery: findManyViewsQuery } = useFindManyRecordsQuery({
objectNameSingular: CoreObjectNameSingular.View,
});
const [mutate] = useMutation< const [mutate] = useMutation<
SyncRemoteTableMutation, SyncRemoteTableMutation,
@ -23,15 +34,24 @@ export const useSyncRemoteTable = () => {
const syncRemoteTable = useCallback( const syncRemoteTable = useCallback(
async (input: RemoteTableInput) => { async (input: RemoteTableInput) => {
return await mutate({ const remoteTable = await mutate({
variables: { variables: {
input, input,
}, },
awaitRefetchQueries: true, awaitRefetchQueries: true,
refetchQueries: [getOperationName(GET_MANY_REMOTE_TABLES) ?? ''], refetchQueries: [getOperationName(GET_MANY_REMOTE_TABLES) ?? ''],
}); });
// TODO: we should return the tables with the columns and store in cache instead of refetching
await refetchObjectMetadataItems();
await apolloClient.query({
query: findManyViewsQuery,
fetchPolicy: 'network-only',
});
return remoteTable;
}, },
[mutate], [apolloClient, findManyViewsQuery, mutate, refetchObjectMetadataItems],
); );
return { return {

View File

@ -5,6 +5,7 @@ import { getOperationName } from '@apollo/client/utilities';
import { UNSYNC_REMOTE_TABLE } from '@/databases/graphql/mutations/unsyncRemoteTable'; import { UNSYNC_REMOTE_TABLE } from '@/databases/graphql/mutations/unsyncRemoteTable';
import { GET_MANY_REMOTE_TABLES } from '@/databases/graphql/queries/findManyRemoteTables'; import { GET_MANY_REMOTE_TABLES } from '@/databases/graphql/queries/findManyRemoteTables';
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient'; import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems';
import { import {
RemoteTableInput, RemoteTableInput,
UnsyncRemoteTableMutation, UnsyncRemoteTableMutation,
@ -13,6 +14,8 @@ import {
export const useUnsyncRemoteTable = () => { export const useUnsyncRemoteTable = () => {
const apolloMetadataClient = useApolloMetadataClient(); const apolloMetadataClient = useApolloMetadataClient();
const { refetch: refetchObjectMetadataItems } =
useFindManyObjectMetadataItems();
const [mutate] = useMutation< const [mutate] = useMutation<
UnsyncRemoteTableMutation, UnsyncRemoteTableMutation,
@ -23,15 +26,19 @@ export const useUnsyncRemoteTable = () => {
const unsyncRemoteTable = useCallback( const unsyncRemoteTable = useCallback(
async (input: RemoteTableInput) => { async (input: RemoteTableInput) => {
return await mutate({ const remoteTable = await mutate({
variables: { variables: {
input, input,
}, },
awaitRefetchQueries: true, awaitRefetchQueries: true,
refetchQueries: [getOperationName(GET_MANY_REMOTE_TABLES) ?? ''], refetchQueries: [getOperationName(GET_MANY_REMOTE_TABLES) ?? ''],
}); });
await refetchObjectMetadataItems();
return remoteTable;
}, },
[mutate], [mutate, refetchObjectMetadataItems],
); );
return { return {

View File

@ -28,7 +28,7 @@ export const useFindManyObjectMetadataItems = ({
const { enqueueSnackBar } = useSnackBar(); const { enqueueSnackBar } = useSnackBar();
const { data, loading, error } = useQuery< const { data, loading, error, refetch } = useQuery<
ObjectMetadataItemsQuery, ObjectMetadataItemsQuery,
ObjectMetadataItemsQueryVariables ObjectMetadataItemsQueryVariables
>(FIND_MANY_OBJECT_METADATA_ITEMS, { >(FIND_MANY_OBJECT_METADATA_ITEMS, {
@ -59,5 +59,6 @@ export const useFindManyObjectMetadataItems = ({
objectMetadataItems, objectMetadataItems,
loading, loading,
error, error,
refetch,
}; };
}; };

View File

@ -106,7 +106,21 @@ export const useRecordActionBar = ({
recordIndexId: objectMetadataItem.namePlural, recordIndexId: objectMetadataItem.namePlural,
}); });
const isRemoteObject = objectMetadataItem.isRemote;
const baseActions: ContextMenuEntry[] = useMemo( const baseActions: ContextMenuEntry[] = useMemo(
() => [
{
label: `${progress === undefined ? `Export` : `Export (${progress}%)`}`,
Icon: IconFileExport,
accent: 'default',
onClick: () => download(),
},
],
[download, progress],
);
const deletionActions: ContextMenuEntry[] = useMemo(
() => [ () => [
{ {
label: 'Delete', label: 'Delete',
@ -130,17 +144,9 @@ export const useRecordActionBar = ({
/> />
), ),
}, },
{
label: `${progress === undefined ? `Export` : `Export (${progress}%)`}`,
Icon: IconFileExport,
accent: 'default',
onClick: () => download(),
},
], ],
[ [
handleDeleteClick, handleDeleteClick,
download,
progress,
selectedRecordIds, selectedRecordIds,
isDeleteRecordsModalOpen, isDeleteRecordsModalOpen,
setIsDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen,
@ -160,8 +166,9 @@ export const useRecordActionBar = ({
return { return {
setContextMenuEntries: useCallback(() => { setContextMenuEntries: useCallback(() => {
setContextMenuEntries([ setContextMenuEntries([
...(isRemoteObject ? [] : deletionActions),
...baseActions, ...baseActions,
...(isFavorite && hasOnlyOneRecordSelected ...(!isRemoteObject && isFavorite && hasOnlyOneRecordSelected
? [ ? [
{ {
label: 'Remove from favorites', label: 'Remove from favorites',
@ -170,7 +177,7 @@ export const useRecordActionBar = ({
}, },
] ]
: []), : []),
...(!isFavorite && hasOnlyOneRecordSelected ...(!isRemoteObject && !isFavorite && hasOnlyOneRecordSelected
? [ ? [
{ {
label: 'Add to favorites', label: 'Add to favorites',
@ -182,9 +189,11 @@ export const useRecordActionBar = ({
]); ]);
}, [ }, [
baseActions, baseActions,
deletionActions,
handleFavoriteButtonClick, handleFavoriteButtonClick,
hasOnlyOneRecordSelected, hasOnlyOneRecordSelected,
isFavorite, isFavorite,
isRemoteObject,
setContextMenuEntries, setContextMenuEntries,
]), ]),
@ -209,12 +218,15 @@ export const useRecordActionBar = ({
}, },
] ]
: []), : []),
...(isRemoteObject ? [] : deletionActions),
...baseActions, ...baseActions,
]); ]);
}, [ }, [
baseActions, baseActions,
dataExecuteQuickActionOnmentEnabled, dataExecuteQuickActionOnmentEnabled,
deletionActions,
handleExecuteQuickActionOnClick, handleExecuteQuickActionOnClick,
isRemoteObject,
setActionBarEntriesState, setActionBarEntriesState,
]), ]),
}; };

View File

@ -47,6 +47,7 @@ type FieldInputProps = {
onEscape?: FieldInputEvent; onEscape?: FieldInputEvent;
onTab?: FieldInputEvent; onTab?: FieldInputEvent;
onShiftTab?: FieldInputEvent; onShiftTab?: FieldInputEvent;
isReadOnly?: boolean;
}; };
export const FieldInput = ({ export const FieldInput = ({
@ -58,6 +59,7 @@ export const FieldInput = ({
onShiftTab, onShiftTab,
onTab, onTab,
onClickOutside, onClickOutside,
isReadOnly,
}: FieldInputProps) => { }: FieldInputProps) => {
const { fieldDefinition } = useContext(FieldContext); const { fieldDefinition } = useContext(FieldContext);
@ -136,7 +138,7 @@ export const FieldInput = ({
onShiftTab={onShiftTab} onShiftTab={onShiftTab}
/> />
) : isFieldBoolean(fieldDefinition) ? ( ) : isFieldBoolean(fieldDefinition) ? (
<BooleanFieldInput onSubmit={onSubmit} /> <BooleanFieldInput onSubmit={onSubmit} readonly={isReadOnly} />
) : isFieldRating(fieldDefinition) ? ( ) : isFieldRating(fieldDefinition) ? (
<RatingFieldInput onSubmit={onSubmit} /> <RatingFieldInput onSubmit={onSubmit} />
) : isFieldSelect(fieldDefinition) ? ( ) : isFieldSelect(fieldDefinition) ? (

View File

@ -92,6 +92,7 @@ export const RecordInlineCell = ({ readonly }: RecordInlineCellProps) => {
onTab={handleTab} onTab={handleTab}
onShiftTab={handleShiftTab} onShiftTab={handleShiftTab}
onClickOutside={handleClickOutside} onClickOutside={handleClickOutside}
isReadOnly={readonly}
/> />
} }
displayModeContent={<FieldDisplay />} displayModeContent={<FieldDisplay />}

View File

@ -47,6 +47,7 @@ export const RecordTableRow = ({ recordId, rowIndex }: RecordTableRowProps) => {
objectNameSingular: objectMetadataItem.nameSingular, objectNameSingular: objectMetadataItem.nameSingular,
}) + recordId, }) + recordId,
isSelected: currentRowSelected, isSelected: currentRowSelected,
isReadOnly: objectMetadataItem.isRemote ?? false,
}} }}
> >
<tr <tr

View File

@ -5,6 +5,7 @@ type RecordTableRowContextProps = {
recordId: string; recordId: string;
rowIndex: number; rowIndex: number;
isSelected: boolean; isSelected: boolean;
isReadOnly: boolean;
}; };
export const RecordTableRowContext = createContext<RecordTableRowContextProps>( export const RecordTableRowContext = createContext<RecordTableRowContextProps>(

View File

@ -4,6 +4,7 @@ import { FieldDisplay } from '@/object-record/record-field/components/FieldDispl
import { FieldInput } from '@/object-record/record-field/components/FieldInput'; import { FieldInput } from '@/object-record/record-field/components/FieldInput';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent'; import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus'; import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus';
import { RecordTableCellContainer } from '@/object-record/record-table/record-table-cell/components/RecordTableCellContainer'; import { RecordTableCellContainer } from '@/object-record/record-table/record-table-cell/components/RecordTableCellContainer';
import { useCloseRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useCloseRecordTableCell'; import { useCloseRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useCloseRecordTableCell';
@ -21,6 +22,7 @@ export const RecordTableCell = ({
const { moveLeft, moveRight, moveDown } = useRecordTableMoveFocus(); const { moveLeft, moveRight, moveDown } = useRecordTableMoveFocus();
const { entityId, fieldDefinition } = useContext(FieldContext); const { entityId, fieldDefinition } = useContext(FieldContext);
const { isReadOnly } = useContext(RecordTableRowContext);
const handleEnter: FieldInputEvent = (persistField) => { const handleEnter: FieldInputEvent = (persistField) => {
upsertRecord(persistField); upsertRecord(persistField);
@ -78,6 +80,7 @@ export const RecordTableCell = ({
onShiftTab={handleShiftTab} onShiftTab={handleShiftTab}
onSubmit={handleSubmit} onSubmit={handleSubmit}
onTab={handleTab} onTab={handleTab}
isReadOnly={isReadOnly}
/> />
} }
nonEditModeContent={<FieldDisplay />} nonEditModeContent={<FieldDisplay />}

View File

@ -90,7 +90,7 @@ export const RecordTableCellContainer = ({
openTableCell(); openTableCell();
}; };
const { isSelected } = useContext(RecordTableRowContext); const { isSelected, isReadOnly } = useContext(RecordTableRowContext);
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState); const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState); const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
@ -146,7 +146,8 @@ export const RecordTableCellContainer = ({
hasSoftFocus && hasSoftFocus &&
!isCurrentTableCellInEditMode && !isCurrentTableCellInEditMode &&
!editModeContentOnly && !editModeContentOnly &&
(!isFirstColumn || !isEmpty); (!isFirstColumn || !isEmpty) &&
!isReadOnly;
return ( return (
<StyledTd <StyledTd

View File

@ -23,7 +23,7 @@ export const DEFAULT_CELL_SCOPE: HotkeyScope = {
}; };
export const useOpenRecordTableCell = () => { export const useOpenRecordTableCell = () => {
const { pathToShowPage } = useContext(RecordTableRowContext); const { pathToShowPage, isReadOnly } = useContext(RecordTableRowContext);
const { setCurrentTableCellInEditMode } = useCurrentTableCellEditMode(); const { setCurrentTableCellInEditMode } = useCurrentTableCellEditMode();
const setHotkeyScope = useSetHotkeyScope(); const setHotkeyScope = useSetHotkeyScope();
@ -46,6 +46,10 @@ export const useOpenRecordTableCell = () => {
const openTableCell = useRecoilCallback( const openTableCell = useRecoilCallback(
() => (options?: { initialValue?: string }) => { () => (options?: { initialValue?: string }) => {
if (isReadOnly) {
return;
}
if (isFirstColumnCell && !isEmpty) { if (isFirstColumnCell && !isEmpty) {
leaveTableFocus(); leaveTableFocus();
navigate(pathToShowPage); navigate(pathToShowPage);
@ -70,15 +74,16 @@ export const useOpenRecordTableCell = () => {
} }
}, },
[ [
isReadOnly,
isFirstColumnCell, isFirstColumnCell,
isEmpty, isEmpty,
leaveTableFocus,
navigate,
pathToShowPage,
setDragSelectionStartEnabled, setDragSelectionStartEnabled,
setCurrentTableCellInEditMode, setCurrentTableCellInEditMode,
initFieldInputDraftValue, initFieldInputDraftValue,
customCellHotkeyScope, customCellHotkeyScope,
leaveTableFocus,
navigate,
pathToShowPage,
setHotkeyScope, setHotkeyScope,
], ],
); );

View File

@ -22,6 +22,7 @@ type SettingsObjectFieldItemTableRowProps = {
fieldMetadataItem: FieldMetadataItem; fieldMetadataItem: FieldMetadataItem;
identifierType?: Nullable<FieldIdentifierType>; identifierType?: Nullable<FieldIdentifierType>;
variant?: 'field-type' | 'identifier'; variant?: 'field-type' | 'identifier';
isRemoteObjectField?: boolean;
}; };
export const StyledObjectFieldTableRow = styled(TableRow)` export const StyledObjectFieldTableRow = styled(TableRow)`
@ -43,6 +44,7 @@ export const SettingsObjectFieldItemTableRow = ({
fieldMetadataItem, fieldMetadataItem,
identifierType, identifierType,
variant = 'field-type', variant = 'field-type',
isRemoteObjectField,
}: SettingsObjectFieldItemTableRowProps) => { }: SettingsObjectFieldItemTableRowProps) => {
const theme = useTheme(); const theme = useTheme();
const { getIcon } = useIcons(); const { getIcon } = useIcons();
@ -76,7 +78,11 @@ export const SettingsObjectFieldItemTableRow = ({
</StyledNameTableCell> </StyledNameTableCell>
<TableCell> <TableCell>
{variant === 'field-type' && {variant === 'field-type' &&
(fieldMetadataItem.isCustom ? 'Custom' : 'Standard')} (isRemoteObjectField
? 'Remote'
: fieldMetadataItem.isCustom
? 'Custom'
: 'Standard')}
{variant === 'identifier' && {variant === 'identifier' &&
!!identifierType && !!identifierType &&
(identifierType === 'label' ? 'Record text' : 'Record image')} (identifierType === 'label' ? 'Record text' : 'Record image')}

View File

@ -4,7 +4,8 @@ import styled from '@emotion/styled';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { SettingsDataModelIsCustomTag } from '@/settings/data-model/objects/SettingsDataModelIsCustomTag'; import { SettingsDataModelObjectTypeTag } from '@/settings/data-model/objects/SettingsDataModelObjectTypeTag';
import { getObjectTypeLabel } from '@/settings/data-model/utils/getObjectTypeLabel';
import { useIcons } from '@/ui/display/icon/hooks/useIcons'; import { useIcons } from '@/ui/display/icon/hooks/useIcons';
import { TableCell } from '@/ui/layout/table/components/TableCell'; import { TableCell } from '@/ui/layout/table/components/TableCell';
import { TableRow } from '@/ui/layout/table/components/TableRow'; import { TableRow } from '@/ui/layout/table/components/TableRow';
@ -42,6 +43,7 @@ export const SettingsObjectItemTableRow = ({
}); });
const { getIcon } = useIcons(); const { getIcon } = useIcons();
const Icon = getIcon(objectItem.icon); const Icon = getIcon(objectItem.icon);
const objectTypeLabel = getObjectTypeLabel(objectItem);
return ( return (
<StyledObjectTableRow key={objectItem.namePlural} onClick={onClick}> <StyledObjectTableRow key={objectItem.namePlural} onClick={onClick}>
@ -52,7 +54,7 @@ export const SettingsObjectItemTableRow = ({
{objectItem.labelPlural} {objectItem.labelPlural}
</StyledNameTableCell> </StyledNameTableCell>
<TableCell> <TableCell>
<SettingsDataModelIsCustomTag isCustom={objectItem.isCustom} /> <SettingsDataModelObjectTypeTag objectTypeLabel={objectTypeLabel} />
</TableCell> </TableCell>
<TableCell align="right"> <TableCell align="right">
{objectItem.fields.filter((field) => !field.isSystem).length} {objectItem.fields.filter((field) => !field.isSystem).length}

View File

@ -2,8 +2,10 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { IconArchive, IconDotsVertical, IconPencil } from 'twenty-ui'; import { IconArchive, IconDotsVertical, IconPencil } from 'twenty-ui';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { SettingsSummaryCard } from '@/settings/components/SettingsSummaryCard'; import { SettingsSummaryCard } from '@/settings/components/SettingsSummaryCard';
import { SettingsDataModelIsCustomTag } from '@/settings/data-model/objects/SettingsDataModelIsCustomTag'; import { SettingsDataModelObjectTypeTag } from '@/settings/data-model/objects/SettingsDataModelObjectTypeTag';
import { getObjectTypeLabel } from '@/settings/data-model/utils/getObjectTypeLabel';
import { useIcons } from '@/ui/display/icon/hooks/useIcons'; import { useIcons } from '@/ui/display/icon/hooks/useIcons';
import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
@ -13,14 +15,14 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
type SettingsObjectSummaryCardProps = { type SettingsObjectSummaryCardProps = {
objectMetadataItem: ObjectMetadataItem;
iconKey?: string; iconKey?: string;
isCustom: boolean;
name: string; name: string;
onDeactivate: () => void; onDeactivate: () => void;
onEdit: () => void; onEdit: () => void;
}; };
const StyledIsCustomTag = styled(SettingsDataModelIsCustomTag)` const StyledObjectTypeTag = styled(SettingsDataModelObjectTypeTag)`
box-sizing: border-box; box-sizing: border-box;
height: ${({ theme }) => theme.spacing(6)}; height: ${({ theme }) => theme.spacing(6)};
`; `;
@ -28,8 +30,8 @@ const StyledIsCustomTag = styled(SettingsDataModelIsCustomTag)`
const dropdownId = 'settings-object-edit-about-menu-dropdown'; const dropdownId = 'settings-object-edit-about-menu-dropdown';
export const SettingsObjectSummaryCard = ({ export const SettingsObjectSummaryCard = ({
objectMetadataItem,
iconKey = '', iconKey = '',
isCustom,
name, name,
onDeactivate, onDeactivate,
onEdit, onEdit,
@ -50,6 +52,8 @@ export const SettingsObjectSummaryCard = ({
closeDropdown(); closeDropdown();
}; };
const objectTypeLabel = getObjectTypeLabel(objectMetadataItem);
return ( return (
<SettingsSummaryCard <SettingsSummaryCard
title={ title={
@ -60,7 +64,7 @@ export const SettingsObjectSummaryCard = ({
} }
rightComponent={ rightComponent={
<> <>
<StyledIsCustomTag isCustom={isCustom} /> <StyledObjectTypeTag objectTypeLabel={objectTypeLabel} />
<Dropdown <Dropdown
dropdownId={dropdownId} dropdownId={dropdownId}
clickableComponent={ clickableComponent={

View File

@ -1,18 +0,0 @@
import { Tag } from '@/ui/display/tag/components/Tag';
type SettingsDataModelIsCustomTagProps = {
className?: string;
isCustom?: boolean;
};
export const SettingsDataModelIsCustomTag = ({
className,
isCustom,
}: SettingsDataModelIsCustomTagProps) => (
<Tag
className={className}
color={isCustom ? 'orange' : 'blue'}
text={isCustom ? 'Custom' : 'Standard'}
weight="medium"
/>
);

View File

@ -2,15 +2,13 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { SettingsDataModelIsCustomTag } from '@/settings/data-model/objects/SettingsDataModelIsCustomTag'; import { SettingsDataModelObjectTypeTag } from '@/settings/data-model/objects/SettingsDataModelObjectTypeTag';
import { getObjectTypeLabel } from '@/settings/data-model/utils/getObjectTypeLabel';
import { useIcons } from '@/ui/display/icon/hooks/useIcons'; import { useIcons } from '@/ui/display/icon/hooks/useIcons';
export type SettingsDataModelObjectSummaryProps = { export type SettingsDataModelObjectSummaryProps = {
className?: string; className?: string;
objectMetadataItem: Pick< objectMetadataItem: ObjectMetadataItem;
ObjectMetadataItem,
'icon' | 'isCustom' | 'labelPlural'
>;
}; };
const StyledObjectSummary = styled.div` const StyledObjectSummary = styled.div`
@ -35,6 +33,7 @@ export const SettingsDataModelObjectSummary = ({
const { getIcon } = useIcons(); const { getIcon } = useIcons();
const ObjectIcon = getIcon(objectMetadataItem.icon); const ObjectIcon = getIcon(objectMetadataItem.icon);
const objectTypeLabel = getObjectTypeLabel(objectMetadataItem);
return ( return (
<StyledObjectSummary className={className}> <StyledObjectSummary className={className}>
@ -42,7 +41,7 @@ export const SettingsDataModelObjectSummary = ({
<ObjectIcon size={theme.icon.size.sm} stroke={theme.icon.stroke.md} /> <ObjectIcon size={theme.icon.size.sm} stroke={theme.icon.stroke.md} />
{objectMetadataItem.labelPlural} {objectMetadataItem.labelPlural}
</StyledObjectName> </StyledObjectName>
<SettingsDataModelIsCustomTag isCustom={objectMetadataItem.isCustom} /> <SettingsDataModelObjectTypeTag objectTypeLabel={objectTypeLabel} />
</StyledObjectSummary> </StyledObjectSummary>
); );
}; };

View File

@ -0,0 +1,21 @@
import { ObjectTypeLabel } from '@/settings/data-model/utils/getObjectTypeLabel';
import { Tag } from '@/ui/display/tag/components/Tag';
type SettingsDataModelObjectTypeTagProps = {
objectTypeLabel: ObjectTypeLabel;
className?: string;
};
export const SettingsDataModelObjectTypeTag = ({
className,
objectTypeLabel,
}: SettingsDataModelObjectTypeTagProps) => {
return (
<Tag
className={className}
color={objectTypeLabel.labelColor}
text={objectTypeLabel.labelText}
weight="medium"
/>
);
};

View File

@ -0,0 +1,39 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
export type ObjectTypeLabel =
| StandardObjectTypeLabel
| CustomObjectTypeLabel
| RemoteObjectTypeLabel;
type StandardObjectTypeLabel = {
labelText: 'Standard';
labelColor: 'blue';
};
type CustomObjectTypeLabel = {
labelText: 'Custom';
labelColor: 'orange';
};
type RemoteObjectTypeLabel = {
labelText: 'Remote';
labelColor: 'green';
};
export const getObjectTypeLabel = (
objectMetadataItem: ObjectMetadataItem,
): ObjectTypeLabel =>
objectMetadataItem.isCustom
? {
labelText: 'Custom',
labelColor: 'orange',
}
: objectMetadataItem.isRemote
? {
labelText: 'Remote',
labelColor: 'green',
}
: {
labelText: 'Standard',
labelColor: 'blue',
};

View File

@ -63,7 +63,7 @@ export const SettingsIntegrationDatabaseTablesListCard = ({
}); });
} }
}, },
[connectionId, syncRemoteTable, items, unsyncRemoteTable], [items, syncRemoteTable, connectionId, unsyncRemoteTable],
); );
const rowRightComponent = useCallback( const rowRightComponent = useCallback(

View File

@ -88,6 +88,11 @@ export const RecordShowPage = () => {
].join(' ') ].join(' ')
: `${labelIdentifierFieldValue}`; : `${labelIdentifierFieldValue}`;
// Temporarily since we don't have relations for remote objects yet
if (objectMetadataItem.isRemote) {
return null;
}
return ( return (
<PageContainer> <PageContainer>
<PageTitle title={pageName} /> <PageTitle title={pageName} />

View File

@ -102,7 +102,7 @@ export const SettingsObjectDetail = () => {
<SettingsObjectSummaryCard <SettingsObjectSummaryCard
iconKey={activeObjectMetadataItem.icon ?? undefined} iconKey={activeObjectMetadataItem.icon ?? undefined}
name={activeObjectMetadataItem.labelPlural || ''} name={activeObjectMetadataItem.labelPlural || ''}
isCustom={activeObjectMetadataItem.isCustom} objectMetadataItem={activeObjectMetadataItem}
onDeactivate={handleDisableObject} onDeactivate={handleDisableObject}
onEdit={() => navigate('./edit')} onEdit={() => navigate('./edit')}
/> />
@ -150,6 +150,7 @@ export const SettingsObjectDetail = () => {
: 'field-type' : 'field-type'
} }
fieldMetadataItem={activeMetadataField} fieldMetadataItem={activeMetadataField}
isRemoteObjectField={activeObjectMetadataItem.isRemote}
ActionIcon={ ActionIcon={
<SettingsObjectFieldActiveActionDropdown <SettingsObjectFieldActiveActionDropdown
isCustomField={!!activeMetadataField.isCustom} isCustomField={!!activeMetadataField.isCustom}

View File

@ -140,6 +140,7 @@ export const SettingsObjectNewFieldStep1 = () => {
<SettingsObjectFieldItemTableRow <SettingsObjectFieldItemTableRow
key={activeMetadataField.id} key={activeMetadataField.id}
fieldMetadataItem={activeMetadataField} fieldMetadataItem={activeMetadataField}
isRemoteObjectField={activeObjectMetadataItem.isRemote}
ActionIcon={ ActionIcon={
isLabelIdentifierField({ isLabelIdentifierField({
fieldMetadataItem: activeMetadataField, fieldMetadataItem: activeMetadataField,

View File

@ -21,6 +21,7 @@ export enum FeatureFlagKeys {
IsAirtableIntegrationEnabled = 'IS_AIRTABLE_INTEGRATION_ENABLED', IsAirtableIntegrationEnabled = 'IS_AIRTABLE_INTEGRATION_ENABLED',
IsPostgreSQLIntegrationEnabled = 'IS_POSTGRESQL_INTEGRATION_ENABLED', IsPostgreSQLIntegrationEnabled = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
IsMultiSelectEnabled = 'IS_MULTI_SELECT_ENABLED', IsMultiSelectEnabled = 'IS_MULTI_SELECT_ENABLED',
IsRelationForRemoteObjectsEnabled = 'IS_RELATION_FOR_REMOTE_OBJECTS_ENABLED',
} }
@Entity({ name: 'featureFlag', schema: 'core' }) @Entity({ name: 'featureFlag', schema: 'core' })

View File

@ -1,4 +1,5 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { import {
NestjsQueryGraphQLModule, NestjsQueryGraphQLModule,
@ -15,6 +16,8 @@ import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { ObjectMetadataResolver } from 'src/engine/metadata-modules/object-metadata/object-metadata.resolver'; import { ObjectMetadataResolver } from 'src/engine/metadata-modules/object-metadata/object-metadata.resolver';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { ObjectMetadataService } from './object-metadata.service'; import { ObjectMetadataService } from './object-metadata.service';
import { ObjectMetadataEntity } from './object-metadata.entity'; import { ObjectMetadataEntity } from './object-metadata.entity';
@ -32,9 +35,11 @@ import { ObjectMetadataDTO } from './dtos/object-metadata.dto';
[ObjectMetadataEntity, FieldMetadataEntity, RelationMetadataEntity], [ObjectMetadataEntity, FieldMetadataEntity, RelationMetadataEntity],
'metadata', 'metadata',
), ),
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
DataSourceModule, DataSourceModule,
WorkspaceMigrationModule, WorkspaceMigrationModule,
WorkspaceMigrationRunnerModule, WorkspaceMigrationRunnerModule,
FeatureFlagModule,
], ],
services: [ObjectMetadataService], services: [ObjectMetadataService],
resolvers: [ resolvers: [

View File

@ -7,7 +7,12 @@ import { InjectRepository } from '@nestjs/typeorm';
import console from 'console'; import console from 'console';
import { FindManyOptions, FindOneOptions, Repository } from 'typeorm'; import {
DataSource,
FindManyOptions,
FindOneOptions,
Repository,
} from 'typeorm';
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm'; import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import { Query, QueryOptions } from '@ptc-org/nestjs-query-core'; import { Query, QueryOptions } from '@ptc-org/nestjs-query-core';
@ -46,9 +51,14 @@ import {
createForeignKeyDeterministicUuid, createForeignKeyDeterministicUuid,
createRelationDeterministicUuid, createRelationDeterministicUuid,
} from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util'; } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util';
import { buildWorkspaceMigrationsForCustomObject } from 'src/engine/metadata-modules/object-metadata/utils/build-workspace-migrations-for-custom-object.util'; import { createWorkspaceMigrationsForCustomObject } from 'src/engine/metadata-modules/object-metadata/utils/create-workspace-migrations-for-custom-object.util';
import { buildWorkspaceMigrationsForRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/build-workspace-migrations-for-remote-object.util'; import { createWorkspaceMigrationsForRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/create-workspace-migrations-for-remote-object.util';
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util'; import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
import {
FeatureFlagEntity,
FeatureFlagKeys,
} from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
import { ObjectMetadataEntity } from './object-metadata.entity'; import { ObjectMetadataEntity } from './object-metadata.entity';
@ -70,6 +80,8 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
private readonly typeORMService: TypeORMService, private readonly typeORMService: TypeORMService,
private readonly workspaceMigrationService: WorkspaceMigrationService, private readonly workspaceMigrationService: WorkspaceMigrationService,
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
@InjectRepository(FeatureFlagEntity, 'core')
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
) { ) {
super(objectMetadataRepository); super(objectMetadataRepository);
} }
@ -322,27 +334,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
[], [],
}); });
const { eventObjectMetadata } = await this.createEventRelation(
objectMetadataInput.workspaceId,
createdObjectMetadata,
);
const { activityTargetObjectMetadata } =
await this.createActivityTargetRelation(
objectMetadataInput.workspaceId,
createdObjectMetadata,
);
const { favoriteObjectMetadata } = await this.createFavoriteRelation(
objectMetadataInput.workspaceId,
createdObjectMetadata,
);
const { attachmentObjectMetadata } = await this.createAttachmentRelation(
objectMetadataInput.workspaceId,
createdObjectMetadata,
);
const dataSourceMetadata = const dataSourceMetadata =
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail( await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
createdObjectMetadata.workspaceId, createdObjectMetadata.workspaceId,
@ -351,27 +342,12 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
const workspaceDataSource = const workspaceDataSource =
await this.typeORMService.connectToDataSource(dataSourceMetadata); await this.typeORMService.connectToDataSource(dataSourceMetadata);
await this.workspaceMigrationService.createCustomMigration( await this.createObjectRelationsMetadataAndMigrations(
generateMigrationName(`create-${createdObjectMetadata.nameSingular}`), objectMetadataInput,
createdObjectMetadata.workspaceId, createdObjectMetadata,
isCustom lastDataSourceMetadata,
? buildWorkspaceMigrationsForCustomObject( workspaceDataSource,
createdObjectMetadata, objectMetadataInput.isRemote,
activityTargetObjectMetadata,
attachmentObjectMetadata,
eventObjectMetadata,
favoriteObjectMetadata,
)
: await buildWorkspaceMigrationsForRemoteObject(
createdObjectMetadata,
activityTargetObjectMetadata,
attachmentObjectMetadata,
eventObjectMetadata,
favoriteObjectMetadata,
lastDataSourceMetadata.schema,
objectMetadataInput.remoteTablePrimaryKeyColumnType ?? 'uuid',
workspaceDataSource,
),
); );
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations( await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
@ -483,6 +459,67 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
await this.objectMetadataRepository.delete({ workspaceId }); await this.objectMetadataRepository.delete({ workspaceId });
} }
private async createObjectRelationsMetadataAndMigrations(
objectMetadataInput: CreateObjectInput,
createdObjectMetadata: ObjectMetadataEntity,
lastDataSourceMetadata: DataSourceEntity,
workspaceDataSource: DataSource | undefined,
isRemoteObject: boolean = false,
) {
const isRelationEnabledForRemoteObjects =
await this.isRelationEnabledForRemoteObjects(
objectMetadataInput.workspaceId,
);
if (isRemoteObject && !isRelationEnabledForRemoteObjects) {
return;
}
const { eventObjectMetadata } = await this.createEventRelation(
objectMetadataInput.workspaceId,
createdObjectMetadata,
);
const { activityTargetObjectMetadata } =
await this.createActivityTargetRelation(
objectMetadataInput.workspaceId,
createdObjectMetadata,
);
const { favoriteObjectMetadata } = await this.createFavoriteRelation(
objectMetadataInput.workspaceId,
createdObjectMetadata,
);
const { attachmentObjectMetadata } = await this.createAttachmentRelation(
objectMetadataInput.workspaceId,
createdObjectMetadata,
);
return this.workspaceMigrationService.createCustomMigration(
generateMigrationName(`create-${createdObjectMetadata.nameSingular}`),
createdObjectMetadata.workspaceId,
isRemoteObject
? await createWorkspaceMigrationsForRemoteObject(
createdObjectMetadata,
activityTargetObjectMetadata,
attachmentObjectMetadata,
eventObjectMetadata,
favoriteObjectMetadata,
lastDataSourceMetadata.schema,
objectMetadataInput.remoteTablePrimaryKeyColumnType ?? 'uuid',
workspaceDataSource,
)
: createWorkspaceMigrationsForCustomObject(
createdObjectMetadata,
activityTargetObjectMetadata,
attachmentObjectMetadata,
eventObjectMetadata,
favoriteObjectMetadata,
),
);
}
private async createActivityTargetRelation( private async createActivityTargetRelation(
workspaceId: string, workspaceId: string,
createdObjectMetadata: ObjectMetadataEntity, createdObjectMetadata: ObjectMetadataEntity,
@ -855,4 +892,14 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
return { favoriteObjectMetadata }; return { favoriteObjectMetadata };
} }
private async isRelationEnabledForRemoteObjects(workspaceId: string) {
const featureFlag = await this.featureFlagRepository.findOneBy({
workspaceId,
key: FeatureFlagKeys.IsRelationForRemoteObjectsEnabled,
value: true,
});
return featureFlag && featureFlag.value;
}
} }

View File

@ -9,7 +9,7 @@ import {
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
export const buildWorkspaceMigrationsForCustomObject = ( export const createWorkspaceMigrationsForCustomObject = (
createdObjectMetadata: ObjectMetadataEntity, createdObjectMetadata: ObjectMetadataEntity,
activityTargetObjectMetadata: ObjectMetadataEntity, activityTargetObjectMetadata: ObjectMetadataEntity,
attachmentObjectMetadata: ObjectMetadataEntity, attachmentObjectMetadata: ObjectMetadataEntity,

View File

@ -47,7 +47,7 @@ const buildCommentForRemoteObjectForeignKey = async (
return `@graphql(${JSON.stringify(parsedComment)})`; return `@graphql(${JSON.stringify(parsedComment)})`;
}; };
export const buildWorkspaceMigrationsForRemoteObject = async ( export const createWorkspaceMigrationsForRemoteObject = async (
createdObjectMetadata: ObjectMetadataEntity, createdObjectMetadata: ObjectMetadataEntity,
activityTargetObjectMetadata: ObjectMetadataEntity, activityTargetObjectMetadata: ObjectMetadataEntity,
attachmentObjectMetadata: ObjectMetadataEntity, attachmentObjectMetadata: ObjectMetadataEntity,
@ -74,19 +74,6 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
} satisfies WorkspaceMigrationColumnCreate, } satisfies WorkspaceMigrationColumnCreate,
], ],
}, },
{
name: computeObjectTargetTable(activityTargetObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
isForeignKey: true,
}),
columnType: remoteTablePrimaryKeyColumnType,
},
],
},
{ {
name: computeObjectTargetTable(activityTargetObjectMetadata), name: computeObjectTargetTable(activityTargetObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER, action: WorkspaceMigrationTableActionType.ALTER,
@ -117,19 +104,6 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
} satisfies WorkspaceMigrationColumnCreate, } satisfies WorkspaceMigrationColumnCreate,
], ],
}, },
{
name: computeObjectTargetTable(attachmentObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
isForeignKey: true,
}),
columnType: remoteTablePrimaryKeyColumnType,
},
],
},
{ {
name: computeObjectTargetTable(attachmentObjectMetadata), name: computeObjectTargetTable(attachmentObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER, action: WorkspaceMigrationTableActionType.ALTER,
@ -160,19 +134,6 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
} satisfies WorkspaceMigrationColumnCreate, } satisfies WorkspaceMigrationColumnCreate,
], ],
}, },
{
name: computeObjectTargetTable(eventObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
isForeignKey: true,
}),
columnType: remoteTablePrimaryKeyColumnType,
},
],
},
{ {
name: computeObjectTargetTable(eventObjectMetadata), name: computeObjectTargetTable(eventObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER, action: WorkspaceMigrationTableActionType.ALTER,
@ -203,19 +164,6 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
} satisfies WorkspaceMigrationColumnCreate, } satisfies WorkspaceMigrationColumnCreate,
], ],
}, },
{
name: computeObjectTargetTable(favoriteObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
isForeignKey: true,
}),
columnType: remoteTablePrimaryKeyColumnType,
},
],
},
{ {
name: computeObjectTargetTable(favoriteObjectMetadata), name: computeObjectTargetTable(favoriteObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER, action: WorkspaceMigrationTableActionType.ALTER,

View File

@ -197,7 +197,7 @@ export class RemoteTableService {
description: 'Remote table', description: 'Remote table',
dataSourceId: dataSourceMetatada.id, dataSourceId: dataSourceMetatada.id,
workspaceId: workspaceId, workspaceId: workspaceId,
icon: 'IconUser', icon: 'IconPlug',
isRemote: true, isRemote: true,
remoteTablePrimaryKeyColumnType: remoteTableIdColumn.udtName, remoteTablePrimaryKeyColumnType: remoteTableIdColumn.udtName,
} satisfies CreateObjectInput); } satisfies CreateObjectInput);
@ -213,7 +213,7 @@ export class RemoteTableService {
objectMetadataId: objectMetadata.id, objectMetadataId: objectMetadata.id,
isRemoteCreation: true, isRemoteCreation: true,
isNullable: true, isNullable: true,
icon: 'IconUser', icon: 'IconPlug',
} satisfies CreateFieldInput); } satisfies CreateFieldInput);
if (column.columnName === 'id') { if (column.columnName === 'id') {

View File

@ -59,6 +59,7 @@ export class AddStandardIdCommand extends CommandRunner {
IS_AIRTABLE_INTEGRATION_ENABLED: true, IS_AIRTABLE_INTEGRATION_ENABLED: true,
IS_POSTGRESQL_INTEGRATION_ENABLED: true, IS_POSTGRESQL_INTEGRATION_ENABLED: true,
IS_MULTI_SELECT_ENABLED: false, IS_MULTI_SELECT_ENABLED: false,
IS_RELATION_FOR_REMOTE_OBJECTS_ENABLED: false,
}, },
); );
const standardFieldMetadataCollection = this.standardFieldFactory.create( const standardFieldMetadataCollection = this.standardFieldFactory.create(
@ -74,6 +75,7 @@ export class AddStandardIdCommand extends CommandRunner {
IS_AIRTABLE_INTEGRATION_ENABLED: true, IS_AIRTABLE_INTEGRATION_ENABLED: true,
IS_POSTGRESQL_INTEGRATION_ENABLED: true, IS_POSTGRESQL_INTEGRATION_ENABLED: true,
IS_MULTI_SELECT_ENABLED: false, IS_MULTI_SELECT_ENABLED: false,
IS_RELATION_FOR_REMOTE_OBJECTS_ENABLED: false,
}, },
); );