Setup relations for remote objects (#5149)

New strategy:
- add settings field on FieldMetadata. Contains a boolean isIdField and
for numbers, a precision
- if idField, the graphql scalar returned will be a GraphQL id. This
will allow the app to work even for ids that are not uuid
- remove globals dateScalar and numberScalar modes. These were not used
- set limit as Integer
- check manually in query runner mutations that we send a valid id

Todo left:
- remove WorkspaceBuildSchemaOptions since this is not used anymore.
Will do in another PR

---------

Co-authored-by: Thomas Trompette <thomast@twenty.com>
Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
Thomas Trompette
2024-04-26 14:37:34 +02:00
committed by GitHub
parent dc576d0818
commit 224c8d361b
71 changed files with 616 additions and 223 deletions

View File

@ -51,7 +51,7 @@ const mocks: MockedResponse[] = [
$filter: ActivityTargetFilterInput
$orderBy: ActivityTargetOrderByInput
$lastCursor: String
$limit: Float
$limit: Int
) {
activityTargets(
filter: $filter
@ -105,7 +105,7 @@ const mocks: MockedResponse[] = [
$filter: ActivityFilterInput
$orderBy: ActivityOrderByInput
$lastCursor: String
$limit: Float
$limit: Int
) {
activities(
filter: $filter

View File

@ -36,7 +36,7 @@ const mocks: MockedResponse[] = [
$filter: ActivityTargetFilterInput
$orderBy: ActivityTargetOrderByInput
$lastCursor: String
$limit: Float
$limit: Int
) {
activityTargets(
filter: $filter

View File

@ -16,7 +16,7 @@ const mocks: MockedResponse[] = [
{
request: {
query: gql`
query FindOneWorkspaceMember($objectRecordId: UUID!) {
query FindOneWorkspaceMember($objectRecordId: ID!) {
workspaceMember(filter: { id: { eq: $objectRecordId } }) {
__typename
colorScheme

View File

@ -17,7 +17,7 @@ const mocks: MockedResponse[] = [
request: {
query: gql`
mutation UpdateOneActivity(
$idToUpdate: UUID!
$idToUpdate: ID!
$input: ActivityUpdateInput!
) {
updateActivity(id: $idToUpdate, data: $input) {

View File

@ -177,7 +177,7 @@ export const mocks = [
{
request: {
query: gql`
mutation DeleteOneFavorite($idToDelete: UUID!) {
mutation DeleteOneFavorite($idToDelete: ID!) {
deleteFavorite(id: $idToDelete) {
id
}
@ -197,7 +197,7 @@ export const mocks = [
request: {
query: gql`
mutation UpdateOneFavorite(
$idToUpdate: UUID!
$idToUpdate: ID!
$input: FavoriteUpdateInput!
) {
updateFavorite(id: $idToUpdate, data: $input) {

View File

@ -48,6 +48,7 @@ export const FIND_MANY_OBJECT_METADATA_ITEMS = gql`
nameSingular
namePlural
isSystem
isRemote
}
toFieldMetadataId
}
@ -60,6 +61,7 @@ export const FIND_MANY_OBJECT_METADATA_ITEMS = gql`
nameSingular
namePlural
isSystem
isRemote
}
fromFieldMetadataId
}

View File

@ -26,7 +26,7 @@ export const findManyViewsQuery = gql`
$filter: ViewFilterInput
$orderBy: ViewOrderByInput
$lastCursor: String
$limit: Float
$limit: Int
) {
views(
filter: $filter

View File

@ -21,6 +21,7 @@ export type FieldMetadataItem = Omit<
| 'toRelationMetadata'
| 'defaultValue'
| 'options'
| 'settings'
| 'relationDefinition'
> & {
__typename?: string;
@ -28,7 +29,7 @@ export type FieldMetadataItem = Omit<
| (Pick<Relation, 'id' | 'toFieldMetadataId' | 'relationType'> & {
toObjectMetadata: Pick<
Relation['toObjectMetadata'],
'id' | 'nameSingular' | 'namePlural' | 'isSystem'
'id' | 'nameSingular' | 'namePlural' | 'isSystem' | 'isRemote'
>;
})
| null;
@ -36,7 +37,7 @@ export type FieldMetadataItem = Omit<
| (Pick<Relation, 'id' | 'fromFieldMetadataId' | 'relationType'> & {
fromObjectMetadata: Pick<
Relation['fromObjectMetadata'],
'id' | 'nameSingular' | 'namePlural' | 'isSystem'
'id' | 'nameSingular' | 'namePlural' | 'isSystem' | 'isRemote'
>;
})
| null;

View File

@ -2,10 +2,15 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
export const isObjectMetadataAvailableForRelation = (
objectMetadataItem: Pick<ObjectMetadataItem, 'isSystem' | 'nameSingular'>,
objectMetadataItem: Pick<
ObjectMetadataItem,
'isSystem' | 'nameSingular' | 'isRemote'
>,
) => {
return (
!objectMetadataItem.isSystem ||
objectMetadataItem.nameSingular === CoreObjectNameSingular.WorkspaceMember
(!objectMetadataItem.isSystem ||
objectMetadataItem.nameSingular ===
CoreObjectNameSingular.WorkspaceMember) &&
!objectMetadataItem.isRemote
);
};

View File

@ -1,8 +1,6 @@
import { useContext } from 'react';
import { EntityChip, EntityChipVariant } from 'twenty-ui';
import { useMapToObjectRecordIdentifier } from '@/object-metadata/hooks/useMapToObjectRecordIdentifier';
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
@ -25,16 +23,8 @@ export const RecordChip = ({
objectNameSingular,
});
// Will only exists if the chip is inside a record table.
// This is temporary until we have the show page for remote objects.
const { isReadOnly } = useContext(RecordTableRowContext);
const objectRecordIdentifier = mapToObjectRecordIdentifier(record);
const linkToEntity = isReadOnly
? undefined
: objectRecordIdentifier.linkToShowPage;
return (
<EntityChip
entityId={record.id}
@ -43,7 +33,7 @@ export const RecordChip = ({
avatarUrl={
getImageAbsoluteURIOrBase64(objectRecordIdentifier.avatarUrl) || ''
}
linkToEntity={linkToEntity}
linkToEntity={objectRecordIdentifier.linkToShowPage}
maxWidth={maxWidth}
className={className}
variant={variant}

View File

@ -1,7 +1,7 @@
import { gql } from '@apollo/client';
export const query = gql`
mutation DeleteOnePerson($idToDelete: UUID!) {
mutation DeleteOnePerson($idToDelete: ID!) {
deletePerson(id: $idToDelete) {
id
}

View File

@ -3,7 +3,7 @@ import { gql } from '@apollo/client';
export { responseData } from './useUpdateOneRecord';
export const query = gql`
mutation ExecuteQuickActionOnOnePerson($idToExecuteQuickActionOn: UUID!) {
mutation ExecuteQuickActionOnOnePerson($idToExecuteQuickActionOn: ID!) {
executeQuickActionOnPerson(id: $idToExecuteQuickActionOn) {
__typename
xLink {

View File

@ -5,7 +5,7 @@ export const query = gql`
$filter: PersonFilterInput
$orderBy: PersonOrderByInput
$lastCursor: String
$limit: Float
$limit: Int
) {
people(
filter: $filter

View File

@ -3,7 +3,7 @@ import { gql } from '@apollo/client';
import { responseData as person } from './useUpdateOneRecord';
export const query = gql`
query FindOnePerson($objectRecordId: UUID!) {
query FindOnePerson($objectRecordId: ID!) {
person(filter: { id: { eq: $objectRecordId } }) {
__typename
xLink {

View File

@ -1,7 +1,7 @@
import { gql } from '@apollo/client';
export const query = gql`
mutation UpdateOnePerson($idToUpdate: UUID!, $input: PersonUpdateInput!) {
mutation UpdateOnePerson($idToUpdate: ID!, $input: PersonUpdateInput!) {
updatePerson(id: $idToUpdate, data: $input) {
__typename
xLink {

View File

@ -26,7 +26,7 @@ export const useDeleteOneRecordMutation = ({
);
const deleteOneRecordMutation = gql`
mutation DeleteOne${capitalizedObjectName}($idToDelete: UUID!) {
mutation DeleteOne${capitalizedObjectName}($idToDelete: ID!) {
${mutationResponseField}(id: $idToDelete) {
id
}

View File

@ -39,7 +39,7 @@ export const useExecuteQuickActionOnOneRecordMutation = ({
});
const executeQuickActionOnOneRecordMutation = gql`
mutation ExecuteQuickActionOnOne${capitalizedObjectName}($idToExecuteQuickActionOn: UUID!) {
mutation ExecuteQuickActionOnOne${capitalizedObjectName}($idToExecuteQuickActionOn: ID!) {
${graphQLFieldForExecuteQuickActionOnOneRecordMutation}(id: $idToExecuteQuickActionOn) ${mapObjectMetadataToGraphQLQuery(
{
objectMetadataItems,

View File

@ -23,7 +23,7 @@ export const useFindDuplicateRecordsQuery = ({
const findDuplicateRecordsQuery = gql`
query FindDuplicate${capitalize(
objectMetadataItem.nameSingular,
)}($id: UUID) {
)}($id: ID!) {
${getFindDuplicateRecordsQueryResponseField(
objectMetadataItem.nameSingular,
)}(id: $id) {

View File

@ -22,7 +22,7 @@ export const useFindOneRecordQuery = ({
const findOneRecordQuery = gql`
query FindOne${capitalize(
objectMetadataItem.nameSingular,
)}($objectRecordId: UUID!) {
)}($objectRecordId: ID!) {
${objectMetadataItem.nameSingular}(filter: {
id: {
eq: $objectRecordId
@ -32,7 +32,7 @@ export const useFindOneRecordQuery = ({
objectMetadataItem,
depth,
})}
}
},
`;
return {

View File

@ -35,7 +35,7 @@ export const useUpdateOneRecordMutation = ({
);
const updateOneRecordMutation = gql`
mutation UpdateOne${capitalizedObjectName}($idToUpdate: UUID!, $input: ${capitalizedObjectName}UpdateInput!) {
mutation UpdateOne${capitalizedObjectName}($idToUpdate: ID!, $input: ${capitalizedObjectName}UpdateInput!) {
${mutationResponseField}(id: $idToUpdate, data: $input) ${mapObjectMetadataToGraphQLQuery(
{
objectMetadataItems,

View File

@ -47,7 +47,7 @@ export const useGenerateFindManyRecordsForMultipleMetadataItemsQuery = ({
const limitPerMetadataItemArray = capitalizedObjectNameSingulars
.map(
(capitalizedObjectNameSingular) =>
`$limit${capitalizedObjectNameSingular}: Float`,
`$limit${capitalizedObjectNameSingular}: Int`,
)
.join(', ');

View File

@ -21,7 +21,7 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
const query = gql`
mutation UpdateOnePerson($idToUpdate: UUID!, $input: PersonUpdateInput!) {
mutation UpdateOnePerson($idToUpdate: ID!, $input: PersonUpdateInput!) {
updatePerson(id: $idToUpdate, data: $input) {
__typename
xLink {

View File

@ -21,7 +21,7 @@ const mocks: MockedResponse[] = [
request: {
query: gql`
mutation UpdateOneCompany(
$idToUpdate: UUID!
$idToUpdate: ID!
$input: CompanyUpdateInput!
) {
updateCompany(id: $idToUpdate, data: $input) {

View File

@ -8,7 +8,7 @@ import { isFieldDateValue } from '@/object-record/record-field/types/guards/isFi
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
import { isFieldFullNameValue } from '@/object-record/record-field/types/guards/isFieldFullNameValue';
import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect';
import { isFieldMultiSelectValue } from '@/object-record/record-field/types/guards/isFieldMultiSelectValue';
import { isFieldMultiSelectValue } from '@/object-record/record-field/types/guards/isFieldMultiSelectValue.ts';
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
import { isFieldRawJsonValue } from '@/object-record/record-field/types/guards/isFieldRawJsonValue';
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';

View File

@ -13,7 +13,7 @@ const query = gql`
$filterNameSingular: NameSingularFilterInput
$orderByNameSingular: NameSingularOrderByInput
$lastCursorNameSingular: String
$limitNameSingular: Float
$limitNameSingular: Int
) {
namePlural(
filter: $filterNameSingular

View File

@ -24,7 +24,7 @@ query FindMany${capitalize(
objectMetadataItem.nameSingular,
)}FilterInput, $orderBy: ${capitalize(
objectMetadataItem.nameSingular,
)}OrderByInput, $lastCursor: String, $limit: Float) {
)}OrderByInput, $lastCursor: String, $limit: Int) {
${
objectMetadataItem.namePlural
}(filter: $filter, orderBy: $orderBy, first: $limit, after: $lastCursor){

View File

@ -5,7 +5,7 @@ export const query = gql`
$filter: PersonFilterInput
$orderBy: PersonOrderByInput
$lastCursor: String
$limit: Float = 60
$limit: Int = 60
) {
people(
filter: $filter

View File

@ -65,7 +65,12 @@ export const ShowPageRightContainer = ({
const { activeTabIdState } = useTabList(TAB_LIST_COMPONENT_ID);
const activeTabId = useRecoilValue(activeTabIdState);
const shouldDisplayCalendarTab = useIsFeatureEnabled('IS_CALENDAR_ENABLED');
const shouldDisplayCalendarTab =
useIsFeatureEnabled('IS_CALENDAR_ENABLED') &&
(targetableObject.targetObjectNameSingular ===
CoreObjectNameSingular.Company ||
targetableObject.targetObjectNameSingular ===
CoreObjectNameSingular.Person);
const shouldDisplayLogTab = useIsFeatureEnabled('IS_EVENT_OBJECT_ENABLED');
const shouldDisplayEmailsTab =