Prevent remote object updates (#4804)
Backend: Adding a new util function that throw an error if the objectMetadata is remote Frontend: hiding the save button when remote Also renaming `useObjectMetadataItemForSettings` since this hook is used in other places than settings and is not in the settings repo. Name can definitely be challenged! --------- Co-authored-by: Thomas Trompette <thomast@twenty.com>
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||||
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
||||||
@ -9,7 +9,7 @@ import { GraphQLView } from '@/views/types/GraphQLView';
|
|||||||
import { getObjectMetadataItemViews } from '@/views/utils/getObjectMetadataItemViews';
|
import { getObjectMetadataItemViews } from '@/views/utils/getObjectMetadataItemViews';
|
||||||
|
|
||||||
export const ObjectMetadataNavItems = () => {
|
export const ObjectMetadataNavItems = () => {
|
||||||
const { activeObjectMetadataItems } = useObjectMetadataItemForSettings();
|
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
const currentPath = useLocation().pathname;
|
const currentPath = useLocation().pathname;
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import {
|
|||||||
query,
|
query,
|
||||||
responseData,
|
responseData,
|
||||||
variables,
|
variables,
|
||||||
} from '@/object-metadata/hooks/__mocks__/useObjectMetadataItemForSettings';
|
} from '@/object-metadata/hooks/__mocks__/useFilteredObjectMetadataItems';
|
||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||||
|
|
||||||
@ -36,14 +36,14 @@ const Wrapper = ({ children }: { children: ReactNode }) => (
|
|||||||
|
|
||||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||||
|
|
||||||
describe('useObjectMetadataItemForSettings', () => {
|
describe('useFilteredObjectMetadataItems', () => {
|
||||||
it('should findActiveObjectMetadataItemBySlug', async () => {
|
it('should findActiveObjectMetadataItemBySlug', async () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() => {
|
() => {
|
||||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||||
setMetadataItems(mockObjectMetadataItems);
|
setMetadataItems(mockObjectMetadataItems);
|
||||||
|
|
||||||
return useObjectMetadataItemForSettings();
|
return useFilteredObjectMetadataItems();
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
wrapper: Wrapper,
|
wrapper: Wrapper,
|
||||||
@ -63,7 +63,7 @@ describe('useObjectMetadataItemForSettings', () => {
|
|||||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||||
setMetadataItems(mockObjectMetadataItems);
|
setMetadataItems(mockObjectMetadataItems);
|
||||||
|
|
||||||
return useObjectMetadataItemForSettings();
|
return useFilteredObjectMetadataItems();
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
wrapper: Wrapper,
|
wrapper: Wrapper,
|
||||||
@ -85,7 +85,7 @@ describe('useObjectMetadataItemForSettings', () => {
|
|||||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||||
setMetadataItems(mockObjectMetadataItems);
|
setMetadataItems(mockObjectMetadataItems);
|
||||||
|
|
||||||
return useObjectMetadataItemForSettings();
|
return useFilteredObjectMetadataItems();
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
wrapper: Wrapper,
|
wrapper: Wrapper,
|
||||||
@ -4,7 +4,7 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
|
|||||||
|
|
||||||
import { getObjectSlug } from '../utils/getObjectSlug';
|
import { getObjectSlug } from '../utils/getObjectSlug';
|
||||||
|
|
||||||
export const useObjectMetadataItemForSettings = () => {
|
export const useFilteredObjectMetadataItems = () => {
|
||||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||||
|
|
||||||
const activeObjectMetadataItems = objectMetadataItems.filter(
|
const activeObjectMetadataItems = objectMetadataItems.filter(
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
|
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
|
||||||
import { validateMetadataLabel } from '@/object-metadata/utils/validateMetadataLabel';
|
import { validateMetadataLabel } from '@/object-metadata/utils/validateMetadataLabel';
|
||||||
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
||||||
@ -58,7 +58,7 @@ export const SettingsObjectFieldRelationForm = ({
|
|||||||
}: SettingsObjectFieldRelationFormProps) => {
|
}: SettingsObjectFieldRelationFormProps) => {
|
||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
const { objectMetadataItems, findObjectMetadataItemById } =
|
const { objectMetadataItems, findObjectMetadataItemById } =
|
||||||
useObjectMetadataItemForSettings();
|
useFilteredObjectMetadataItems();
|
||||||
|
|
||||||
const selectedObjectMetadataItem =
|
const selectedObjectMetadataItem =
|
||||||
(values.objectMetadataId
|
(values.objectMetadataId
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { SettingsDataModelDefaultValueForm } from '@/settings/data-model/components/SettingsDataModelDefaultValue';
|
import { SettingsDataModelDefaultValueForm } from '@/settings/data-model/components/SettingsDataModelDefaultValue';
|
||||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||||
@ -79,7 +79,7 @@ export const SettingsDataModelFieldSettingsFormCard = ({
|
|||||||
relationFieldMetadataItem,
|
relationFieldMetadataItem,
|
||||||
values,
|
values,
|
||||||
}: SettingsDataModelFieldSettingsFormCardProps) => {
|
}: SettingsDataModelFieldSettingsFormCardProps) => {
|
||||||
const { findObjectMetadataItemById } = useObjectMetadataItemForSettings();
|
const { findObjectMetadataItemById } = useFilteredObjectMetadataItems();
|
||||||
|
|
||||||
if (!previewableTypes.includes(fieldMetadataItem.type)) return null;
|
if (!previewableTypes.includes(fieldMetadataItem.type)) return null;
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { recordIndexViewTypeState } from '@/object-record/record-index/states/recordIndexViewTypeState';
|
import { recordIndexViewTypeState } from '@/object-record/record-index/states/recordIndexViewTypeState';
|
||||||
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
||||||
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
|
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
|
||||||
@ -20,7 +20,10 @@ export const RecordIndexPageHeader = ({
|
|||||||
const objectNamePlural = useParams().objectNamePlural ?? '';
|
const objectNamePlural = useParams().objectNamePlural ?? '';
|
||||||
|
|
||||||
const { findObjectMetadataItemByNamePlural } =
|
const { findObjectMetadataItemByNamePlural } =
|
||||||
useObjectMetadataItemForSettings();
|
useFilteredObjectMetadataItems();
|
||||||
|
|
||||||
|
const objectMetadataItem =
|
||||||
|
findObjectMetadataItemByNamePlural(objectNamePlural);
|
||||||
|
|
||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
const Icon = getIcon(
|
const Icon = getIcon(
|
||||||
@ -29,12 +32,13 @@ export const RecordIndexPageHeader = ({
|
|||||||
|
|
||||||
const recordIndexViewType = useRecoilValue(recordIndexViewTypeState);
|
const recordIndexViewType = useRecoilValue(recordIndexViewTypeState);
|
||||||
|
|
||||||
|
const canAddRecord =
|
||||||
|
recordIndexViewType === ViewType.Table && !objectMetadataItem?.isRemote;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageHeader title={capitalize(objectNamePlural)} Icon={Icon}>
|
<PageHeader title={capitalize(objectNamePlural)} Icon={Icon}>
|
||||||
<PageHotkeysEffect onAddButtonClick={createRecord} />
|
<PageHotkeysEffect onAddButtonClick={createRecord} />
|
||||||
{recordIndexViewType === ViewType.Table && (
|
{canAddRecord && <PageAddButton onClick={createRecord} />}
|
||||||
<PageAddButton onClick={createRecord} />
|
|
||||||
)}
|
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { IconPlus, IconSettings } from 'twenty-ui';
|
|||||||
|
|
||||||
import { LABEL_IDENTIFIER_FIELD_METADATA_TYPES } from '@/object-metadata/constants/LabelIdentifierFieldMetadataTypes';
|
import { LABEL_IDENTIFIER_FIELD_METADATA_TYPES } from '@/object-metadata/constants/LabelIdentifierFieldMetadataTypes';
|
||||||
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
|
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
|
||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
|
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
|
||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { getActiveFieldMetadataItems } from '@/object-metadata/utils/getActiveFieldMetadataItems';
|
import { getActiveFieldMetadataItems } from '@/object-metadata/utils/getActiveFieldMetadataItems';
|
||||||
@ -45,7 +45,7 @@ export const SettingsObjectDetail = () => {
|
|||||||
|
|
||||||
const { objectSlug = '' } = useParams();
|
const { objectSlug = '' } = useParams();
|
||||||
const { findActiveObjectMetadataItemBySlug } =
|
const { findActiveObjectMetadataItemBySlug } =
|
||||||
useObjectMetadataItemForSettings();
|
useFilteredObjectMetadataItems();
|
||||||
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
|
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
|
||||||
|
|
||||||
const activeObjectMetadataItem =
|
const activeObjectMetadataItem =
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import pick from 'lodash.pick';
|
|||||||
import { IconArchive, IconSettings } from 'twenty-ui';
|
import { IconArchive, IconSettings } from 'twenty-ui';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
|
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
|
||||||
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
|
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
|
||||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||||
@ -44,7 +44,7 @@ export const SettingsObjectEdit = () => {
|
|||||||
|
|
||||||
const { objectSlug = '' } = useParams();
|
const { objectSlug = '' } = useParams();
|
||||||
const { findActiveObjectMetadataItemBySlug } =
|
const { findActiveObjectMetadataItemBySlug } =
|
||||||
useObjectMetadataItemForSettings();
|
useFilteredObjectMetadataItems();
|
||||||
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
|
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
|
||||||
|
|
||||||
const activeObjectMetadataItem =
|
const activeObjectMetadataItem =
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import { isNonEmptyString } from '@sniptt/guards';
|
|||||||
import { IconArchive, IconSettings } from 'twenty-ui';
|
import { IconArchive, IconSettings } from 'twenty-ui';
|
||||||
|
|
||||||
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
|
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
|
||||||
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
|
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
|
||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
|
||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { getFieldSlug } from '@/object-metadata/utils/getFieldSlug';
|
import { getFieldSlug } from '@/object-metadata/utils/getFieldSlug';
|
||||||
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
||||||
@ -52,7 +52,7 @@ export const SettingsObjectFieldEdit = () => {
|
|||||||
|
|
||||||
const { objectSlug = '', fieldSlug = '' } = useParams();
|
const { objectSlug = '', fieldSlug = '' } = useParams();
|
||||||
const { findActiveObjectMetadataItemBySlug } =
|
const { findActiveObjectMetadataItemBySlug } =
|
||||||
useObjectMetadataItemForSettings();
|
useFilteredObjectMetadataItems();
|
||||||
|
|
||||||
const activeObjectMetadataItem =
|
const activeObjectMetadataItem =
|
||||||
findActiveObjectMetadataItemBySlug(objectSlug);
|
findActiveObjectMetadataItemBySlug(objectSlug);
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import styled from '@emotion/styled';
|
|||||||
import { IconMinus, IconPlus, IconSettings } from 'twenty-ui';
|
import { IconMinus, IconPlus, IconSettings } from 'twenty-ui';
|
||||||
|
|
||||||
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
|
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
|
||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
||||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||||
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
||||||
@ -39,7 +39,7 @@ export const SettingsObjectNewFieldStep1 = () => {
|
|||||||
|
|
||||||
const { objectSlug = '' } = useParams();
|
const { objectSlug = '' } = useParams();
|
||||||
const { findActiveObjectMetadataItemBySlug } =
|
const { findActiveObjectMetadataItemBySlug } =
|
||||||
useObjectMetadataItemForSettings();
|
useFilteredObjectMetadataItems();
|
||||||
|
|
||||||
const activeObjectMetadataItem =
|
const activeObjectMetadataItem =
|
||||||
findActiveObjectMetadataItemBySlug(objectSlug);
|
findActiveObjectMetadataItemBySlug(objectSlug);
|
||||||
@ -114,11 +114,13 @@ export const SettingsObjectNewFieldStep1 = () => {
|
|||||||
{ children: 'New Field' },
|
{ children: 'New Field' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<SaveAndCancelButtons
|
{!activeObjectMetadataItem.isRemote && (
|
||||||
isSaveDisabled={!canSave}
|
<SaveAndCancelButtons
|
||||||
onCancel={() => navigate(`/settings/objects/${objectSlug}`)}
|
isSaveDisabled={!canSave}
|
||||||
onSave={handleSave}
|
onCancel={() => navigate(`/settings/objects/${objectSlug}`)}
|
||||||
/>
|
onSave={handleSave}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</SettingsHeaderContainer>
|
</SettingsHeaderContainer>
|
||||||
<StyledSection>
|
<StyledSection>
|
||||||
<H2Title
|
<H2Title
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { IconSettings } from 'twenty-ui';
|
|||||||
import { CachedObjectRecordEdge } from '@/apollo/types/CachedObjectRecordEdge';
|
import { CachedObjectRecordEdge } from '@/apollo/types/CachedObjectRecordEdge';
|
||||||
import { useCreateOneRelationMetadataItem } from '@/object-metadata/hooks/useCreateOneRelationMetadataItem';
|
import { useCreateOneRelationMetadataItem } from '@/object-metadata/hooks/useCreateOneRelationMetadataItem';
|
||||||
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
|
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
|
||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly';
|
import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { modifyRecordFromCache } from '@/object-record/cache/utils/modifyRecordFromCache';
|
import { modifyRecordFromCache } from '@/object-record/cache/utils/modifyRecordFromCache';
|
||||||
@ -46,7 +46,7 @@ export const SettingsObjectNewFieldStep2 = () => {
|
|||||||
findActiveObjectMetadataItemBySlug,
|
findActiveObjectMetadataItemBySlug,
|
||||||
findObjectMetadataItemById,
|
findObjectMetadataItemById,
|
||||||
findObjectMetadataItemByNamePlural,
|
findObjectMetadataItemByNamePlural,
|
||||||
} = useObjectMetadataItemForSettings();
|
} = useFilteredObjectMetadataItems();
|
||||||
|
|
||||||
const activeObjectMetadataItem =
|
const activeObjectMetadataItem =
|
||||||
findActiveObjectMetadataItemBySlug(objectSlug);
|
findActiveObjectMetadataItemBySlug(objectSlug);
|
||||||
@ -300,11 +300,13 @@ export const SettingsObjectNewFieldStep2 = () => {
|
|||||||
{ children: 'New Field' },
|
{ children: 'New Field' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<SaveAndCancelButtons
|
{!activeObjectMetadataItem.isRemote && (
|
||||||
isSaveDisabled={!canSave}
|
<SaveAndCancelButtons
|
||||||
onCancel={() => navigate(`/settings/objects/${objectSlug}`)}
|
isSaveDisabled={!canSave}
|
||||||
onSave={handleSave}
|
onCancel={() => navigate(`/settings/objects/${objectSlug}`)}
|
||||||
/>
|
onSave={handleSave}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</SettingsHeaderContainer>
|
</SettingsHeaderContainer>
|
||||||
<SettingsObjectFieldFormSection
|
<SettingsObjectFieldFormSection
|
||||||
iconKey={formValues.icon}
|
iconKey={formValues.icon}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import styled from '@emotion/styled';
|
|||||||
import { IconChevronRight, IconPlus, IconSettings } from 'twenty-ui';
|
import { IconChevronRight, IconPlus, IconSettings } from 'twenty-ui';
|
||||||
|
|
||||||
import { useDeleteOneObjectMetadataItem } from '@/object-metadata/hooks/useDeleteOneObjectMetadataItem';
|
import { useDeleteOneObjectMetadataItem } from '@/object-metadata/hooks/useDeleteOneObjectMetadataItem';
|
||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
|
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
|
||||||
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
|
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
|
||||||
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
||||||
@ -39,7 +39,7 @@ export const SettingsObjects = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { activeObjectMetadataItems, inactiveObjectMetadataItems } =
|
const { activeObjectMetadataItems, inactiveObjectMetadataItems } =
|
||||||
useObjectMetadataItemForSettings();
|
useFilteredObjectMetadataItems();
|
||||||
const { deleteOneObjectMetadataItem } = useDeleteOneObjectMetadataItem();
|
const { deleteOneObjectMetadataItem } = useDeleteOneObjectMetadataItem();
|
||||||
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
|
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,7 @@ import { EnvironmentService } from 'src/engine/integrations/environment/environm
|
|||||||
import { NotFoundError } from 'src/engine/utils/graphql-errors.util';
|
import { NotFoundError } from 'src/engine/utils/graphql-errors.util';
|
||||||
import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory';
|
import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory';
|
||||||
import { QueryResultGettersFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters.factory';
|
import { QueryResultGettersFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters.factory';
|
||||||
|
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||||
|
|
||||||
import { WorkspaceQueryRunnerOptions } from './interfaces/query-runner-option.interface';
|
import { WorkspaceQueryRunnerOptions } from './interfaces/query-runner-option.interface';
|
||||||
import {
|
import {
|
||||||
@ -217,6 +218,9 @@ export class WorkspaceQueryRunnerService {
|
|||||||
options: WorkspaceQueryRunnerOptions,
|
options: WorkspaceQueryRunnerOptions,
|
||||||
): Promise<Record[] | undefined> {
|
): Promise<Record[] | undefined> {
|
||||||
const { workspaceId, userId, objectMetadataItem } = options;
|
const { workspaceId, userId, objectMetadataItem } = options;
|
||||||
|
|
||||||
|
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||||
|
|
||||||
const computedArgs = await this.queryRunnerArgsFactory.create(
|
const computedArgs = await this.queryRunnerArgsFactory.create(
|
||||||
args,
|
args,
|
||||||
options,
|
options,
|
||||||
@ -273,6 +277,8 @@ export class WorkspaceQueryRunnerService {
|
|||||||
): Promise<Record | undefined> {
|
): Promise<Record | undefined> {
|
||||||
const { workspaceId, userId, objectMetadataItem } = options;
|
const { workspaceId, userId, objectMetadataItem } = options;
|
||||||
|
|
||||||
|
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||||
|
|
||||||
const existingRecord = await this.findOne(
|
const existingRecord = await this.findOne(
|
||||||
{ filter: { id: { eq: args.id } } } as FindOneResolverArgs,
|
{ filter: { id: { eq: args.id } } } as FindOneResolverArgs,
|
||||||
options,
|
options,
|
||||||
@ -318,6 +324,9 @@ export class WorkspaceQueryRunnerService {
|
|||||||
options: WorkspaceQueryRunnerOptions,
|
options: WorkspaceQueryRunnerOptions,
|
||||||
): Promise<Record[] | undefined> {
|
): Promise<Record[] | undefined> {
|
||||||
const { workspaceId, objectMetadataItem } = options;
|
const { workspaceId, objectMetadataItem } = options;
|
||||||
|
|
||||||
|
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||||
|
|
||||||
const maximumRecordAffected = this.environmentService.get(
|
const maximumRecordAffected = this.environmentService.get(
|
||||||
'MUTATION_MAXIMUM_RECORD_AFFECTED',
|
'MUTATION_MAXIMUM_RECORD_AFFECTED',
|
||||||
);
|
);
|
||||||
@ -359,6 +368,9 @@ export class WorkspaceQueryRunnerService {
|
|||||||
options: WorkspaceQueryRunnerOptions,
|
options: WorkspaceQueryRunnerOptions,
|
||||||
): Promise<Record[] | undefined> {
|
): Promise<Record[] | undefined> {
|
||||||
const { workspaceId, userId, objectMetadataItem } = options;
|
const { workspaceId, userId, objectMetadataItem } = options;
|
||||||
|
|
||||||
|
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||||
|
|
||||||
const maximumRecordAffected = this.environmentService.get(
|
const maximumRecordAffected = this.environmentService.get(
|
||||||
'MUTATION_MAXIMUM_RECORD_AFFECTED',
|
'MUTATION_MAXIMUM_RECORD_AFFECTED',
|
||||||
);
|
);
|
||||||
@ -403,6 +415,9 @@ export class WorkspaceQueryRunnerService {
|
|||||||
options: WorkspaceQueryRunnerOptions,
|
options: WorkspaceQueryRunnerOptions,
|
||||||
): Promise<Record | undefined> {
|
): Promise<Record | undefined> {
|
||||||
const { workspaceId, userId, objectMetadataItem } = options;
|
const { workspaceId, userId, objectMetadataItem } = options;
|
||||||
|
|
||||||
|
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||||
|
|
||||||
const query = await this.workspaceQueryBuilderFactory.deleteOne(
|
const query = await this.workspaceQueryBuilderFactory.deleteOne(
|
||||||
args,
|
args,
|
||||||
options,
|
options,
|
||||||
|
|||||||
@ -38,6 +38,7 @@ import {
|
|||||||
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||||
import { DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input';
|
import { DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input';
|
||||||
import { computeCustomName } from 'src/engine/utils/compute-custom-name.util';
|
import { computeCustomName } from 'src/engine/utils/compute-custom-name.util';
|
||||||
|
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FieldMetadataEntity,
|
FieldMetadataEntity,
|
||||||
@ -94,6 +95,10 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
throw new NotFoundException('Object does not exist');
|
throw new NotFoundException('Object does not exist');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fieldMetadataInput.isRemoteCreation) {
|
||||||
|
assertMutationNotOnRemoteObject(objectMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
// Double check in case the service is directly called
|
// Double check in case the service is directly called
|
||||||
if (isEnumFieldMetadataType(fieldMetadataInput.type)) {
|
if (isEnumFieldMetadataType(fieldMetadataInput.type)) {
|
||||||
if (
|
if (
|
||||||
@ -273,6 +278,8 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
throw new NotFoundException('Object does not exist');
|
throw new NotFoundException('Object does not exist');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertMutationNotOnRemoteObject(objectMetadata);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
objectMetadata.labelIdentifierFieldMetadataId ===
|
objectMetadata.labelIdentifierFieldMetadataId ===
|
||||||
existingFieldMetadata.id &&
|
existingFieldMetadata.id &&
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||||
|
|
||||||
|
export const assertMutationNotOnRemoteObject = (
|
||||||
|
objectMetadataItem: ObjectMetadataInterface,
|
||||||
|
) => {
|
||||||
|
if (objectMetadataItem.isRemote) {
|
||||||
|
throw new Error('Remote objects are read-only');
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user