From 8e35edad3087d0a38e6a271d17119b11fef8bd71 Mon Sep 17 00:00:00 2001 From: Marie <51697796+ijreilly@users.noreply.github.com> Date: Tue, 30 Jul 2024 11:47:37 +0200 Subject: [PATCH] Migrate domainName field from text type to links type (#6410) Closes #5759. --- .../useActivityTargetObjectRecords.test.tsx | 14 +- .../command-menu/components/CommandMenu.tsx | 3 +- .../src/modules/companies/types/Company.ts | 8 +- .../favorites/hooks/__mocks__/useFavorites.ts | 12 +- .../modules/favorites/utils/mapFavorites.ts | 5 +- .../mapFieldMetadataToGraphQLQuery.test.tsx | 16 +- .../mapObjectMetadataToGraphQLQuery.test.tsx | 5 + .../object-metadata/utils/getAvatarUrl.ts | 6 +- .../utils/getCompanyDomainName.ts | 14 + .../utils/getObjectMetadataItemsMock.ts | 2 +- .../graphql/types/RecordGqlOperationFilter.ts | 6 + .../__tests__/useToggleEditOnlyInput.test.tsx | 6 +- .../perf/relationFromManyFieldDisplayMock.ts | 13 +- .../utils/isRecordMatchingFilter.spec.ts | 40 ++- .../utils/isRecordMatchingFilter.ts | 18 ++ ...jectRecordsSpreasheetImportDialog.test.tsx | 9 +- .../constants/CompositeFieldImportLabels.ts | 5 + .../hooks/useBuildAvailableFieldsForImport.ts | 18 ++ .../buildRecordFromImportedStructuredRow.ts | 25 +- .../utils/sanitizeRecordInput.ts | 10 +- .../SignInBackgroundMockColumnDefinitions.ts | 2 +- .../SignInBackgroundMockCompanies.ts | 26 +- .../SignInBackgroundMockFilterDefinitions.ts | 2 +- .../graphql/fragments/userQueryFragment.ts | 5 +- .../src/testing/mock-data/activities.ts | 5 +- .../src/testing/mock-data/companies.ts | 156 ++++++--- .../standard-metadata-query-result.ts | 2 +- .../src/testing/mock-data/people.ts | 250 +++++++++------ .../0-23-migrate-domain-to-links.command.ts | 302 ++++++++++++++++++ .../0-23/0-23-upgrade-version.command.ts | 3 + .../0-23/0-23-upgrade-version.module.ts | 2 + .../typeorm-seeds/workspace/companies.ts | 28 +- .../quick-actions/quick-actions.service.ts | 4 +- .../decorators/workspace-field.decorator.ts | 4 +- .../demo-objects-prefill-data/company.ts | 2 +- .../standard-objects-prefill-data/company.ts | 12 +- .../sync-workspace-metadata.command.ts | 7 +- .../repositories/company.repository.ts | 8 +- .../company.workspace-entity.ts | 2 +- .../contact-creation-manager.module.ts | 2 + .../create-company-and-contact.service.ts | 22 ++ .../services/create-company.service.ts | 8 +- .../src/modules/view/services/view.service.ts | 3 + .../src/utils/getCompanyDomainName.ts | 13 + 44 files changed, 888 insertions(+), 217 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-metadata/utils/getCompanyDomainName.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version/0-23/0-23-migrate-domain-to-links.command.ts create mode 100644 packages/twenty-server/src/utils/getCompanyDomainName.ts diff --git a/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivityTargetObjectRecords.test.tsx b/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivityTargetObjectRecords.test.tsx index 2096f8c4a..6d161906d 100644 --- a/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivityTargetObjectRecords.test.tsx +++ b/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivityTargetObjectRecords.test.tsx @@ -1,7 +1,7 @@ -import { ReactNode } from 'react'; import { gql, InMemoryCache } from '@apollo/client'; import { MockedProvider } from '@apollo/client/testing'; import { act, renderHook } from '@testing-library/react'; +import { ReactNode } from 'react'; import { RecoilRoot, useSetRecoilState } from 'recoil'; import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords'; @@ -45,7 +45,11 @@ const activityNode = { company: { id: '89bb825c-171e-4bcc-9cf7-43448d6fb280', name: 'Airbnb', - domainName: 'airbnb.com', + domainName: { + primaryLinkUrl: 'https://www.airbnb.com', + primaryLinkLabel: '', + secondaryLinks: null, + }, }, person: null, activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb230', @@ -90,7 +94,11 @@ cache.writeFragment({ company { id name - domainName + domainName { + primaryLinkUrl + primaryLinkLabel + secondaryLinks + } } person activityId diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx index 6ca530344..39383072a 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx @@ -33,6 +33,7 @@ import { commandMenuCommandsState } from '../states/commandMenuCommandsState'; import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState'; import { Command, CommandType } from '../types/Command'; +import { getCompanyDomainName } from '@/object-metadata/utils/getCompanyDomainName'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { CommandGroup } from './CommandGroup'; import { CommandMenuItem } from './CommandMenuItem'; @@ -429,7 +430,7 @@ export const CommandMenu = () => { placeholderColorSeed={company.id} placeholder={company.name} avatarUrl={getLogoUrlFromDomainName( - company.domainName, + getCompanyDomainName(company), )} /> )} diff --git a/packages/twenty-front/src/modules/companies/types/Company.ts b/packages/twenty-front/src/modules/companies/types/Company.ts index 2bdc5bc88..7596c946a 100644 --- a/packages/twenty-front/src/modules/companies/types/Company.ts +++ b/packages/twenty-front/src/modules/companies/types/Company.ts @@ -5,7 +5,13 @@ export type Company = { updatedAt?: string; deletedAt?: string | null; name: string; - domainName: string; + domainName: + | string + | { + __typename?: 'Links'; + primaryLinkUrl: string; + primaryLinkLabel: string; + }; address: string; accountOwnerId?: string | null; position?: number; diff --git a/packages/twenty-front/src/modules/favorites/hooks/__mocks__/useFavorites.ts b/packages/twenty-front/src/modules/favorites/hooks/__mocks__/useFavorites.ts index 45dfba438..99978a410 100644 --- a/packages/twenty-front/src/modules/favorites/hooks/__mocks__/useFavorites.ts +++ b/packages/twenty-front/src/modules/favorites/hooks/__mocks__/useFavorites.ts @@ -143,7 +143,11 @@ export const mocks = [ primaryLinkLabel secondaryLinks } - domainName + domainName { + primaryLinkUrl + primaryLinkLabel + secondaryLinks + } annualRecurringRevenue { amountMicros currencyCode @@ -273,7 +277,11 @@ export const mocks = [ primaryLinkLabel secondaryLinks } - domainName + domainName { + primaryLinkUrl + primaryLinkLabel + secondaryLinks + } annualRecurringRevenue { amountMicros currencyCode diff --git a/packages/twenty-front/src/modules/favorites/utils/mapFavorites.ts b/packages/twenty-front/src/modules/favorites/utils/mapFavorites.ts index c0e5deff1..b3efb816e 100644 --- a/packages/twenty-front/src/modules/favorites/utils/mapFavorites.ts +++ b/packages/twenty-front/src/modules/favorites/utils/mapFavorites.ts @@ -1,3 +1,4 @@ +import { getCompanyDomainName } from '@/object-metadata/utils/getCompanyDomainName'; import { getLogoUrlFromDomainName } from '~/utils'; import { isDefined } from '~/utils/isDefined'; @@ -19,7 +20,9 @@ export const mapFavorites = (favorites: any) => { ? { id: favorite.company.id, labelIdentifier: favorite.company.name, - avatarUrl: getLogoUrlFromDomainName(favorite.company.domainName), + avatarUrl: getLogoUrlFromDomainName( + getCompanyDomainName(favorite.company), + ), avatarType: 'squared', link: `/object/company/${favorite.company.id}`, } diff --git a/packages/twenty-front/src/modules/object-metadata/utils/__tests__/mapFieldMetadataToGraphQLQuery.test.tsx b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/mapFieldMetadataToGraphQLQuery.test.tsx index fa54aa795..1e40e9526 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/__tests__/mapFieldMetadataToGraphQLQuery.test.tsx +++ b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/mapFieldMetadataToGraphQLQuery.test.tsx @@ -61,6 +61,11 @@ linkedinLink secondaryLinks } domainName +{ + primaryLinkUrl + primaryLinkLabel + secondaryLinks +} annualRecurringRevenue { amountMicros @@ -95,7 +100,11 @@ idealCustomerProfile people: true, xLink: true, linkedinLink: true, - domainName: true, + domainName: { + primaryLinkUrl: true, + primaryLinkLabel: true, + secondaryLinks: true, + }, annualRecurringRevenue: true, createdAt: true, address: { addressStreet1: true }, @@ -136,6 +145,11 @@ linkedinLink secondaryLinks } domainName +{ + primaryLinkUrl + primaryLinkLabel + secondaryLinks +} annualRecurringRevenue { amountMicros diff --git a/packages/twenty-front/src/modules/object-metadata/utils/__tests__/mapObjectMetadataToGraphQLQuery.test.tsx b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/mapObjectMetadataToGraphQLQuery.test.tsx index fac523a61..36b3d5f77 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/__tests__/mapObjectMetadataToGraphQLQuery.test.tsx +++ b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/mapObjectMetadataToGraphQLQuery.test.tsx @@ -61,6 +61,11 @@ linkedinLink secondaryLinks } domainName +{ + primaryLinkUrl + primaryLinkLabel + secondaryLinks +} annualRecurringRevenue { amountMicros diff --git a/packages/twenty-front/src/modules/object-metadata/utils/getAvatarUrl.ts b/packages/twenty-front/src/modules/object-metadata/utils/getAvatarUrl.ts index 97ffc4e38..2abab1463 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/getAvatarUrl.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/getAvatarUrl.ts @@ -5,6 +5,8 @@ import { getLogoUrlFromDomainName } from '~/utils'; import { getImageAbsoluteURI } from '~/utils/image/getImageAbsoluteURI'; import { isDefined } from '~/utils/isDefined'; +import { Company } from '@/companies/types/Company'; +import { getCompanyDomainName } from '@/object-metadata/utils/getCompanyDomainName'; import { getImageIdentifierFieldValue } from './getImageIdentifierFieldValue'; export const getAvatarUrl = ( @@ -17,7 +19,9 @@ export const getAvatarUrl = ( } if (objectNameSingular === CoreObjectNameSingular.Company) { - return getLogoUrlFromDomainName(record.domainName ?? ''); + return getLogoUrlFromDomainName( + getCompanyDomainName(record as Company) ?? '', + ); } if (objectNameSingular === CoreObjectNameSingular.Person) { diff --git a/packages/twenty-front/src/modules/object-metadata/utils/getCompanyDomainName.ts b/packages/twenty-front/src/modules/object-metadata/utils/getCompanyDomainName.ts new file mode 100644 index 000000000..a408c8dbd --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/utils/getCompanyDomainName.ts @@ -0,0 +1,14 @@ +import { Company } from '@/companies/types/Company'; +import { isDefined } from 'twenty-ui'; + +// temporary, to remove once domainName has been fully migrated to Links type +export const getCompanyDomainName = (company: Company) => { + if (!isDefined(company.domainName)) { + return company.domainName; + } + if (typeof company.domainName === 'string') { + return company.domainName; + } else { + return company.domainName.primaryLinkUrl; + } +}; diff --git a/packages/twenty-front/src/modules/object-metadata/utils/getObjectMetadataItemsMock.ts b/packages/twenty-front/src/modules/object-metadata/utils/getObjectMetadataItemsMock.ts index 9953c5767..eff84e050 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/getObjectMetadataItemsMock.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/getObjectMetadataItemsMock.ts @@ -2940,7 +2940,7 @@ export const getObjectMetadataItemsMock = () => { { __typename: 'field', id: '20202020-5e4e-4007-a630-8a2617914889', - type: 'TEXT', + type: 'LINKS', name: 'domainName', label: 'Domain Name', description: diff --git a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts index fd6de3f70..3b81d20d6 100644 --- a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts +++ b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts @@ -85,6 +85,11 @@ export type AddressFilter = { addressPostcode?: StringFilter; }; +export type LinksFilter = { + primaryLinkUrl?: StringFilter; + primaryLinkLabel?: StringFilter; +}; + export type LeafFilter = | UUIDFilter | StringFilter @@ -95,6 +100,7 @@ export type LeafFilter = | FullNameFilter | BooleanFilter | AddressFilter + | LinksFilter | undefined; export type AndObjectRecordFilter = { diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useToggleEditOnlyInput.test.tsx b/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useToggleEditOnlyInput.test.tsx index acc06e733..da69d6e4b 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useToggleEditOnlyInput.test.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useToggleEditOnlyInput.test.tsx @@ -36,7 +36,11 @@ const mocks: MockedResponse[] = [ primaryLinkLabel secondaryLinks } - domainName + domainName { + primaryLinkUrl + primaryLinkLabel + secondaryLinks + } annualRecurringRevenue { amountMicros currencyCode diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/perf/relationFromManyFieldDisplayMock.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/perf/relationFromManyFieldDisplayMock.ts index 30c07ab46..b8e872671 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/perf/relationFromManyFieldDisplayMock.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/perf/relationFromManyFieldDisplayMock.ts @@ -3,7 +3,7 @@ import { FieldMetadataType } from '~/generated-metadata/graphql'; export const fieldValue = [ { __typename: 'Company', - domainName: 'google.com', + domainName: { primaryLinkUrl: 'google.com', primaryLinkLabel: '' }, xLink: { __typename: 'Link', primaryLinkLabel: '', @@ -31,7 +31,7 @@ export const fieldValue = [ }, { __typename: 'Company', - domainName: 'airbnb.com', + domainName: { primaryLinkUrl: 'airbnb.com', primaryLinkLabel: '' }, xLink: { __typename: 'Link', primaryLinkLabel: '', @@ -69,7 +69,7 @@ export const otherPersonMock = { createdAt: '2024-05-01T13:16:29.046Z', company: { __typename: 'Company', - domainName: 'google.com', + domainName: { primaryLinkUrl: 'google.com', primaryLinkLabel: '' }, xLink: { __typename: 'Link', primaryLinkLabel: '', @@ -125,7 +125,7 @@ export const relationFromManyFieldDisplayMock = { createdAt: '2024-05-01T13:16:29.046Z', company: { __typename: 'Company', - domainName: 'google.com', + domainName: { primaryLinkUrl: 'google.com', primaryLinkLabel: '' }, xLink: { __typename: 'Link', primaryLinkLabel: '', @@ -169,7 +169,10 @@ export const relationFromManyFieldDisplayMock = { }, relationFieldValue: { __typename: 'Company', - domainName: 'microsoft.com', + domainName: { + primaryLinkLabel: '', + primaryLinkUrl: 'microsoft.com', + }, xLink: { __typename: 'Link', primaryLinkLabel: '', diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.spec.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.spec.ts index 5e4c21c91..ed0b22070 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.spec.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.spec.ts @@ -2,6 +2,8 @@ import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGq import { getCompaniesMock } from '~/testing/mock-data/companies'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems'; +import { Company } from '@/companies/types/Company'; +import { getCompanyDomainName } from '@/object-metadata/utils/getCompanyDomainName'; import { isRecordMatchingFilter } from './isRecordMatchingFilter'; const companiesMock = getCompaniesMock(); @@ -123,10 +125,19 @@ describe('isRecordMatchingFilter', () => { const companyMockNotInFilter = { ...companiesMock[0], - domainName: companyMockInFilter.domainName + 'Different', + domainName: { + primaryLinkUrl: + getCompanyDomainName(companyMockInFilter as Company) + 'Different', + }, }; - const filter = { domainName: { eq: companyMockInFilter.domainName } }; + const filter = { + domainName: { + primaryLinkUrl: { + eq: getCompanyDomainName(companyMockInFilter as Company), + }, + }, + }; expect( isRecordMatchingFilter({ @@ -228,7 +239,9 @@ describe('isRecordMatchingFilter', () => { and: [ { domainName: { - eq: companyMockInFilter.domainName, + primaryLinkUrl: { + eq: getCompanyDomainName(companyMockInFilter as Company), + }, }, }, { @@ -317,14 +330,23 @@ describe('isRecordMatchingFilter', () => { const companyMockNotInFilter = { ...companiesMock[0], - domainName: companyMockInFilter.domainName + 'Different', + domainName: { + primaryLinkUrl: + getCompanyDomainName(companyMockInFilter as Company) + 'Different', + }, employees: 5, name: companyMockInFilter.name + 'Different', }; const filter: RecordGqlOperationFilter = { and: [ - { domainName: { eq: companyMockInFilter.domainName } }, + { + domainName: { + primaryLinkUrl: { + eq: getCompanyDomainName(companyMockInFilter as Company), + }, + }, + }, { or: [ { employees: { eq: companyMockInFilter.employees } }, @@ -478,13 +500,17 @@ describe('isRecordMatchingFilter', () => { const companyMockNotInFilter = { ...companiesMock[0], name: companyMockInFilter.name + 'Different', - domainName: companyMockInFilter.name + 'Different', + domainName: { primaryLinkUrl: companyMockInFilter.name + 'Different' }, }; const filter = { or: { name: { eq: companyMockInFilter.name }, - domainName: { eq: companyMockInFilter.domainName }, + domainName: { + primaryLinkUrl: { + eq: getCompanyDomainName(companyMockInFilter as Company), + }, + }, }, }; diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts index b5ec81678..53f50c3a9 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts @@ -9,6 +9,7 @@ import { DateFilter, FloatFilter, FullNameFilter, + LinksFilter, NotObjectRecordFilter, OrObjectRecordFilter, RecordGqlOperationFilter, @@ -207,6 +208,23 @@ export const isRecordMatchingFilter = ({ }); }); } + case FieldMetadataType.Links: { + const linksFilter = filterValue as LinksFilter; + + const keys = ['primaryLinkLabel', 'primaryLinkUrl'] as const; + + return keys.some((key) => { + const value = linksFilter[key]; + if (value === undefined) { + return false; + } + + return isMatchingStringFilter({ + stringFilter: value, + value: record[filterKey][key], + }); + }); + } case FieldMetadataType.DateTime: { return isMatchingDateFilter({ dateFilter: filterValue as DateFilter, diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/__tests__/useOpenObjectRecordsSpreasheetImportDialog.test.tsx b/packages/twenty-front/src/modules/object-record/spreadsheet-import/__tests__/useOpenObjectRecordsSpreasheetImportDialog.test.tsx index 712ad24aa..e9eaa3701 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/__tests__/useOpenObjectRecordsSpreasheetImportDialog.test.tsx +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/__tests__/useOpenObjectRecordsSpreasheetImportDialog.test.tsx @@ -36,7 +36,11 @@ const companyMocks = [ primaryLinkLabel secondaryLinks } - domainName + domainName { + primaryLinkUrl + primaryLinkLabel + secondaryLinks + } annualRecurringRevenue { amountMicros currencyCode @@ -64,7 +68,6 @@ const companyMocks = [ variables: { data: [ { - domainName: 'example.com', employees: 0, idealCustomerProfile: true, name: 'Example Company', @@ -157,7 +160,6 @@ describe('useSpreadsheetCompanyImport', () => { { id: companyId, name: 'Example Company', - domainName: 'example.com', idealCustomerProfile: true, employees: '0', }, @@ -167,7 +169,6 @@ describe('useSpreadsheetCompanyImport', () => { { id: companyId, name: 'Example Company', - domainName: 'example.com', __index: 'cbc3985f-dde9-46d1-bae2-c124141700ac', idealCustomerProfile: true, employees: '0', diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/constants/CompositeFieldImportLabels.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/constants/CompositeFieldImportLabels.ts index b7d1ff710..3c5fa16d0 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/constants/CompositeFieldImportLabels.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/constants/CompositeFieldImportLabels.ts @@ -2,6 +2,7 @@ import { FieldAddressValue, FieldCurrencyValue, FieldFullNameValue, + FieldLinksValue, } from '@/object-record/record-field/types/FieldMetadata'; import { CompositeFieldLabels } from '@/object-record/spreadsheet-import/types/CompositeFieldLabels'; import { FieldMetadataType } from '~/generated-metadata/graphql'; @@ -25,4 +26,8 @@ export const COMPOSITE_FIELD_IMPORT_LABELS = { addressLatLabel: 'Latitude', addressLngLabel: 'Longitude', } satisfies CompositeFieldLabels, + [FieldMetadataType.Links]: { + primaryLinkUrlLabel: 'Link URL', + primaryLinkLabelLabel: 'Link Label', + } satisfies Partial>, }; diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useBuildAvailableFieldsForImport.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useBuildAvailableFieldsForImport.ts index 3a8d68d55..7128e6129 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useBuildAvailableFieldsForImport.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useBuildAvailableFieldsForImport.ts @@ -105,6 +105,24 @@ export const useBuildAvailableFieldsForImport = () => { ), }); }); + } else if (fieldMetadataItem.type === FieldMetadataType.Links) { + Object.entries( + COMPOSITE_FIELD_IMPORT_LABELS[FieldMetadataType.Links], + ).forEach(([_, fieldLabel]) => { + availableFieldsForImport.push({ + icon: getIcon(fieldMetadataItem.icon), + label: `${fieldLabel} (${fieldMetadataItem.label})`, + key: `${fieldLabel} (${fieldMetadataItem.name})`, + fieldType: { + type: 'input', + }, + fieldValidationDefinitions: + getSpreadSheetFieldValidationDefinitions( + fieldMetadataItem.type, + `${fieldLabel} (${fieldMetadataItem.label})`, + ), + }); + }); } else { availableFieldsForImport.push({ icon: getIcon(fieldMetadataItem.icon), diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/util/buildRecordFromImportedStructuredRow.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/util/buildRecordFromImportedStructuredRow.ts index 89646bf9a..c354fc2ff 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/util/buildRecordFromImportedStructuredRow.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/util/buildRecordFromImportedStructuredRow.ts @@ -1,5 +1,8 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; -import { FieldAddressValue } from '@/object-record/record-field/types/FieldMetadata'; +import { + FieldAddressValue, + FieldLinksValue, +} from '@/object-record/record-field/types/FieldMetadata'; import { COMPOSITE_FIELD_IMPORT_LABELS } from '@/object-record/spreadsheet-import/constants/CompositeFieldImportLabels'; import { ImportedStructuredRow } from '@/spreadsheet-import/types'; import { isNonEmptyString } from '@sniptt/guards'; @@ -27,6 +30,7 @@ export const buildRecordFromImportedStructuredRow = ( }, CURRENCY: { amountMicrosLabel, currencyCodeLabel }, FULL_NAME: { firstNameLabel, lastNameLabel }, + LINKS: { primaryLinkLabelLabel, primaryLinkUrlLabel }, } = COMPOSITE_FIELD_IMPORT_LABELS; for (const field of fields) { @@ -106,6 +110,25 @@ export const buildRecordFromImportedStructuredRow = ( } break; } + case FieldMetadataType.Links: { + if ( + isDefined( + importedStructuredRow[`${primaryLinkUrlLabel} (${field.name})`] || + importedStructuredRow[`${primaryLinkLabelLabel} (${field.name})`], + ) + ) { + recordToBuild[field.name] = { + primaryLinkLabel: castToString( + importedStructuredRow[`${primaryLinkLabelLabel} (${field.name})`], + ), + primaryLinkUrl: castToString( + importedStructuredRow[`${primaryLinkUrlLabel} (${field.name})`], + ), + secondaryLinks: null, + } satisfies FieldLinksValue; + } + break; + } case FieldMetadataType.Link: if (importedFieldValue !== undefined) { recordToBuild[field.name] = { diff --git a/packages/twenty-front/src/modules/object-record/utils/sanitizeRecordInput.ts b/packages/twenty-front/src/modules/object-record/utils/sanitizeRecordInput.ts index 314fa1e91..54f9bd224 100644 --- a/packages/twenty-front/src/modules/object-record/utils/sanitizeRecordInput.ts +++ b/packages/twenty-front/src/modules/object-record/utils/sanitizeRecordInput.ts @@ -1,6 +1,5 @@ import { isString } from '@sniptt/guards'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { isFieldRelationToOneValue } from '@/object-record/record-field/types/guards/isFieldRelationToOneValue'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; @@ -47,13 +46,14 @@ export const sanitizeRecordInput = ({ .filter(isDefined), ); if ( - objectMetadataItem.nameSingular !== CoreObjectNameSingular.Company || - !isString(filteredResultRecord.domainName) + !( + isDefined(filteredResultRecord.domainName) && + isString(filteredResultRecord.domainName) + ) ) return filteredResultRecord; - return { ...filteredResultRecord, - domainName: getUrlHostName(filteredResultRecord.domainName), + domainName: getUrlHostName(filteredResultRecord.domainName as string), }; }; diff --git a/packages/twenty-front/src/modules/sign-in-background-mock/constants/SignInBackgroundMockColumnDefinitions.ts b/packages/twenty-front/src/modules/sign-in-background-mock/constants/SignInBackgroundMockColumnDefinitions.ts index cb0634772..8a5192949 100644 --- a/packages/twenty-front/src/modules/sign-in-background-mock/constants/SignInBackgroundMockColumnDefinitions.ts +++ b/packages/twenty-front/src/modules/sign-in-background-mock/constants/SignInBackgroundMockColumnDefinitions.ts @@ -10,7 +10,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COLUMN_DEFINITIONS = ( fieldMetadataId: '20202020-5e4e-4007-a630-8a2617914889', label: 'Domain Name', size: 100, - type: FieldMetadataType.Text, + type: FieldMetadataType.Links, metadata: { fieldName: 'domainName', placeHolder: 'Domain Name', diff --git a/packages/twenty-front/src/modules/sign-in-background-mock/constants/SignInBackgroundMockCompanies.ts b/packages/twenty-front/src/modules/sign-in-background-mock/constants/SignInBackgroundMockCompanies.ts index c65dbadf8..d35811fbc 100644 --- a/packages/twenty-front/src/modules/sign-in-background-mock/constants/SignInBackgroundMockCompanies.ts +++ b/packages/twenty-front/src/modules/sign-in-background-mock/constants/SignInBackgroundMockCompanies.ts @@ -2,7 +2,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: '04b2e9f5-0713-40a5-8216-82802401d33e', - domainName: 'qonto.com', + domainName: { primarlyLinkUrl: 'qonto.com', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 1400, name: 'Qonto', @@ -112,7 +112,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: '0d940997-c21e-4ec2-873b-de4264d89025', - domainName: 'google.com', + domainName: { primarlyLinkUrl: 'google.com', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 72_000, name: 'Google', @@ -316,7 +316,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: '118995f3-5d81-46d6-bf83-f7fd33ea6102', - domainName: 'facebook.com', + domainName: { primarlyLinkUrl: 'facebook.com', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 87_000, name: 'Facebook', @@ -450,7 +450,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: '1d3a1c6e-707e-44dc-a1d2-30030bf1a944', - domainName: 'netflix.com', + domainName: { primarlyLinkUrl: 'netflix.com', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 3_000, name: 'Netflix', @@ -503,7 +503,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: '460b6fb1-ed89-413a-b31a-962986e67bb4', - domainName: 'microsoft.com', + domainName: { primarlyLinkUrl: 'microsoft.com', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 221_000, name: 'Microsoft', @@ -686,7 +686,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: '7a93d1e5-3f74-492d-a101-2a70f50a1645', - domainName: 'libeo.io', + domainName: { primarlyLinkUrl: 'libeo.io', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 50, name: 'Libeo', @@ -738,7 +738,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: '89bb825c-171e-4bcc-9cf7-43448d6fb278', - domainName: 'airbnb.com', + domainName: { primarlyLinkUrl: 'airbnb.com', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 6_000, name: 'Airbnb', @@ -791,7 +791,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: '9d162de6-cfbf-4156-a790-e39854dcd4eb', - domainName: 'claap.io', + domainName: { primarlyLinkUrl: 'claap.io', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 7, name: 'Claap', @@ -845,7 +845,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: 'a674fa6c-1455-4c57-afaf-dd5dc086361d', - domainName: 'algolia.com', + domainName: { primarlyLinkUrl: 'algolia.com', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 250, name: 'Algolia', @@ -930,7 +930,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: 'a7bc68d5-f79e-40dd-bd06-c36e6abb4678', - domainName: 'samsung.com', + domainName: { primarlyLinkUrl: 'samsung.com', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 400_000, name: 'Samsung', @@ -1013,7 +1013,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: 'aaffcfbd-f86b-419f-b794-02319abe8637', - domainName: 'hasura.io', + domainName: { primarlyLinkUrl: 'hasura.io', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 17_000, name: 'Hasura', @@ -1067,7 +1067,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: 'f33dc242-5518-4553-9433-42d8eb82834b', - domainName: 'wework.com', + domainName: { primarlyLinkUrl: 'wework.com', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 2_000, name: 'Wework', @@ -1118,7 +1118,7 @@ export const SIGN_IN_BACKGROUND_MOCK_COMPANIES = [ { __typename: 'Company', id: 'fe256b39-3ec3-4fe3-8997-b76aa0bfa408', - domainName: 'linkedin.com', + domainName: { primarlyLinkUrl: 'linkedin.com', primaryLinkLabel: '' }, updatedAt: '2023-11-23T15:38:03.699Z', employees: 20_000, name: 'Linkedin', diff --git a/packages/twenty-front/src/modules/sign-in-background-mock/constants/SignInBackgroundMockFilterDefinitions.ts b/packages/twenty-front/src/modules/sign-in-background-mock/constants/SignInBackgroundMockFilterDefinitions.ts index f53451ad7..6452a6600 100644 --- a/packages/twenty-front/src/modules/sign-in-background-mock/constants/SignInBackgroundMockFilterDefinitions.ts +++ b/packages/twenty-front/src/modules/sign-in-background-mock/constants/SignInBackgroundMockFilterDefinitions.ts @@ -5,7 +5,7 @@ export const SIGN_IN_BACKGROUND_MOCK_FILTER_DEFINITIONS = [ fieldMetadataId: '20202020-5e4e-4007-a630-8a2617914889', label: 'Domain Name', iconName: 'IconLink', - type: 'TEXT', + type: 'LINKS', }, { fieldMetadataId: '20202020-7fbd-41ad-b64d-25a15ff62f04', diff --git a/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts b/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts index 67dfa226e..df3d23412 100644 --- a/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts +++ b/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts @@ -23,7 +23,10 @@ export const USER_QUERY_FRAGMENT = gql` id displayName logo - domainName + domainName { + primaryLinkUrl + primaryLinkLabel + } inviteHash allowImpersonation activationStatus diff --git a/packages/twenty-front/src/testing/mock-data/activities.ts b/packages/twenty-front/src/testing/mock-data/activities.ts index 5f2b45404..7ccb6def2 100644 --- a/packages/twenty-front/src/testing/mock-data/activities.ts +++ b/packages/twenty-front/src/testing/mock-data/activities.ts @@ -110,7 +110,10 @@ export const mockedActivities: Array = [ __typename: 'Company', id: '89bb825c-171e-4bcc-9cf7-43448d6fb280', name: 'Airbnb', - domainName: 'airbnb.com', + domainName: { + primaryLinkLabel: '', + primaryLinkUrl: 'https://www.airbnb.com', + }, }, person: null, activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb230', diff --git a/packages/twenty-front/src/testing/mock-data/companies.ts b/packages/twenty-front/src/testing/mock-data/companies.ts index 8f9a08db7..15f3a9262 100644 --- a/packages/twenty-front/src/testing/mock-data/companies.ts +++ b/packages/twenty-front/src/testing/mock-data/companies.ts @@ -13,7 +13,12 @@ export const getEmptyCompanyMock = () => { return { id: '9231e6ee-4cc2-4c7b-8c55-dff16f4d968a', name: '', - domainName: '', + domainName: { + __typename: 'Links', + primaryLinkUrl: '', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', accountOwner: null, createdAt: null, @@ -51,17 +56,22 @@ export const companiesQueryResult = { name: 'Linkedin', accountOwnerId: null, accountOwner: null, - domainName: 'linkedin.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: 'https://linkedin.com', + primaryLinkUrl: '', + secondaryLinks: null, + }, address: '', position: 1, idealCustomerProfile: true, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -97,7 +107,7 @@ export const companiesQueryResult = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'twitter.com', }, @@ -113,7 +123,7 @@ export const companiesQueryResult = { lastName: 'Duss', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -129,7 +139,7 @@ export const companiesQueryResult = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -149,7 +159,12 @@ export const companiesQueryResult = { idealCustomerProfile: false, accountOwner: null, accountOwnerId: null, - domainName: 'facebook.com', + domainName: { + __typename: 'Links', + primaryLinkUrl: 'https://facebook.com', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', previousEmployees: null, annualRecurringRevenue: { @@ -159,12 +174,12 @@ export const companiesQueryResult = { }, position: 2, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -183,7 +198,12 @@ export const companiesQueryResult = { idealCustomerProfile: false, accountOwner: null, accountOwnerId: null, - domainName: 'qonto.com', + domainName: { + __typename: 'Links', + primaryLinkUrl: 'https://qonto.com', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', previousEmployees: null, annualRecurringRevenue: { @@ -193,12 +213,12 @@ export const companiesQueryResult = { }, position: 3, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -217,7 +237,12 @@ export const companiesQueryResult = { idealCustomerProfile: true, accountOwner: null, accountOwnerId: null, - domainName: 'microsoft.com', + domainName: { + __typename: 'Links', + primaryLinkUrl: 'https://microsoft.com', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', previousEmployees: null, annualRecurringRevenue: { @@ -227,12 +252,12 @@ export const companiesQueryResult = { }, position: 4, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -251,7 +276,12 @@ export const companiesQueryResult = { idealCustomerProfile: true, accountOwner: null, accountOwnerId: null, - domainName: 'airbnb.com', + domainName: { + __typename: 'Links', + primaryLinkUrl: 'https://airbnb.com', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', previousEmployees: null, annualRecurringRevenue: { @@ -261,12 +291,12 @@ export const companiesQueryResult = { }, position: 5, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -285,7 +315,12 @@ export const companiesQueryResult = { idealCustomerProfile: false, accountOwner: null, accountOwnerId: null, - domainName: 'google.com', + domainName: { + __typename: 'Links', + primaryLinkUrl: 'https://google.com', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', previousEmployees: null, annualRecurringRevenue: { @@ -295,12 +330,12 @@ export const companiesQueryResult = { }, position: 6, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -319,7 +354,12 @@ export const companiesQueryResult = { idealCustomerProfile: true, accountOwner: null, accountOwnerId: null, - domainName: 'netflix.com', + domainName: { + __typename: 'Links', + primaryLinkUrl: 'https://netflix.com', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', previousEmployees: null, annualRecurringRevenue: { @@ -329,12 +369,12 @@ export const companiesQueryResult = { }, position: 7, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -353,7 +393,12 @@ export const companiesQueryResult = { idealCustomerProfile: false, accountOwner: null, accountOwnerId: null, - domainName: 'libeo.io', + domainName: { + __typename: 'Links', + primaryLinkUrl: 'https://libeo.io', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', previousEmployees: null, annualRecurringRevenue: { @@ -363,12 +408,12 @@ export const companiesQueryResult = { }, position: 8, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -387,7 +432,12 @@ export const companiesQueryResult = { idealCustomerProfile: false, accountOwner: null, accountOwnerId: null, - domainName: 'claap.io', + domainName: { + __typename: 'Links', + primaryLinkUrl: 'https://claap.com', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', previousEmployees: null, annualRecurringRevenue: { @@ -397,12 +447,12 @@ export const companiesQueryResult = { }, position: 9, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -421,7 +471,12 @@ export const companiesQueryResult = { idealCustomerProfile: false, accountOwner: null, accountOwnerId: null, - domainName: 'hasura.io', + domainName: { + __typename: 'Links', + primaryLinkUrl: 'https://hasura.io', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', previousEmployees: null, annualRecurringRevenue: { @@ -431,12 +486,12 @@ export const companiesQueryResult = { }, position: 10, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -455,7 +510,12 @@ export const companiesQueryResult = { idealCustomerProfile: false, accountOwner: null, accountOwnerId: null, - domainName: 'wework.com', + domainName: { + __typename: 'Links', + primaryLinkUrl: 'https://wework.com', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', previousEmployees: null, annualRecurringRevenue: { @@ -465,12 +525,12 @@ export const companiesQueryResult = { }, position: 11, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -489,7 +549,12 @@ export const companiesQueryResult = { idealCustomerProfile: false, accountOwner: null, accountOwnerId: null, - domainName: 'samsung.com', + domainName: { + __typename: 'Links', + primaryLinkUrl: 'https://samsung.com', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', previousEmployees: null, annualRecurringRevenue: { @@ -499,12 +564,12 @@ export const companiesQueryResult = { }, position: 12, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -523,7 +588,12 @@ export const companiesQueryResult = { idealCustomerProfile: false, accountOwner: null, accountOwnerId: null, - domainName: 'algolia.com', + domainName: { + __typename: 'Links', + primaryLinkUrl: 'https://algolia.com', + primaryLinkLabel: '', + secondaryLinks: null, + }, address: '', previousEmployees: null, annualRecurringRevenue: { @@ -533,12 +603,12 @@ export const companiesQueryResult = { }, position: 13, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, diff --git a/packages/twenty-front/src/testing/mock-data/generated/standard-metadata-query-result.ts b/packages/twenty-front/src/testing/mock-data/generated/standard-metadata-query-result.ts index b537a84c2..cd1e9603a 100644 --- a/packages/twenty-front/src/testing/mock-data/generated/standard-metadata-query-result.ts +++ b/packages/twenty-front/src/testing/mock-data/generated/standard-metadata-query-result.ts @@ -938,7 +938,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = "node": { "__typename": "field", "id": "6b97ae7c-9b97-4a8f-885b-7f8ee3122a7d", - "type": "TEXT", + "type": "LINKS", "name": "domainName", "label": "Domain Name", "description": "The company website URL. We use this url to fetch the company icon", diff --git a/packages/twenty-front/src/testing/mock-data/people.ts b/packages/twenty-front/src/testing/mock-data/people.ts index 64b24546d..b6f61888d 100644 --- a/packages/twenty-front/src/testing/mock-data/people.ts +++ b/packages/twenty-front/src/testing/mock-data/people.ts @@ -64,7 +64,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'twitter.com', }, @@ -93,7 +93,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Callisto', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -109,7 +109,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'singlelink.com', }, @@ -123,11 +123,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Algolia', - domainName: 'algolia.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'algolia.com', + }, address: '', position: 13, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -137,7 +141,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -152,11 +156,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Linkedin', - domainName: 'linkedin.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'linkedin.com', + }, address: '', position: 1, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -166,7 +174,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -199,7 +207,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'twitter.com', }, @@ -220,7 +228,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Palmer', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -236,7 +244,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -250,11 +258,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Linkedin', - domainName: 'linkedin.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'linkedin.com', + }, address: '', position: 1, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -264,7 +276,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -297,7 +309,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'twitter.com', }, @@ -313,7 +325,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Gonzalez', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -329,7 +341,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -343,11 +355,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Qonto', - domainName: 'qonto.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'qonto.com', + }, address: '', position: 3, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -357,7 +373,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -390,7 +406,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'twitter.com', }, @@ -406,7 +422,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Parker', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -422,7 +438,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -436,11 +452,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Qonto', - domainName: 'qonto.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'qonto.com', + }, address: '', position: 3, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -450,7 +470,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -483,7 +503,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'twitter.com', }, @@ -499,7 +519,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Wright', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -515,7 +535,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -529,11 +549,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Microsoft', - domainName: 'microsoft.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'microsoft.com', + }, address: '', position: 4, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -543,7 +567,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -576,7 +600,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'twitter.com', }, @@ -592,7 +616,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Scott', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -608,7 +632,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -622,11 +646,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Microsoft', - domainName: 'microsoft.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'microsoft.com', + }, address: '', position: 4, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -636,7 +664,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -669,7 +697,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'twitter.com', }, @@ -685,7 +713,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Green', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -701,7 +729,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -715,11 +743,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Microsoft', - domainName: 'microsoft.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'microsoft.com', + }, address: '', position: 4, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -729,7 +761,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -762,7 +794,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'twitter.com', }, @@ -778,7 +810,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Baker', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -794,7 +826,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -808,11 +840,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Airbnb', - domainName: 'airbnb.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'airbnb.com', + }, address: '', position: 5, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -822,7 +858,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -855,7 +891,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -871,7 +907,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Nelson', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -887,7 +923,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -901,11 +937,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Airbnb', - domainName: 'airbnb.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'airbnb.com', + }, address: '', position: 5, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -915,7 +955,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -948,7 +988,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -964,7 +1004,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Carter', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -980,7 +1020,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -994,11 +1034,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Airbnb', - domainName: 'airbnb.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'airbnb.com', + }, address: '', position: 5, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1008,7 +1052,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1041,7 +1085,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'twitter.com', }, @@ -1057,7 +1101,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Mitchell', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -1073,7 +1117,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1087,11 +1131,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Google', - domainName: 'google.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'google.com', + }, address: '', position: 6, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1101,7 +1149,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1134,7 +1182,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1150,7 +1198,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Perez', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -1166,7 +1214,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1180,11 +1228,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Google', - domainName: 'google.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'google.com', + }, address: '', position: 6, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1194,7 +1246,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1227,7 +1279,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1243,7 +1295,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Voulzy', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -1259,7 +1311,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1273,11 +1325,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Google', - domainName: 'google.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'google.com', + }, address: '', position: 6, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1287,7 +1343,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1320,7 +1376,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'twitter.com', }, @@ -1336,7 +1392,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Duss', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -1352,7 +1408,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1366,11 +1422,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Google', - domainName: 'google.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'google.com', + }, address: '', position: 6, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1380,7 +1440,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1413,7 +1473,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: 'USD', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1429,7 +1489,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { lastName: 'Vladim', }, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: 'linkedin.com', }, @@ -1445,7 +1505,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { addressLng: null, }, testLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1459,11 +1519,15 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { idealCustomerProfile: false, createdAt: '2024-06-05T09:00:20.412Z', name: 'Google', - domainName: 'google.com', + domainName: { + __typename: 'Links', + primaryLinkLabel: '', + primaryLinkUrl: 'google.com', + }, address: '', position: 6, linkedinLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, @@ -1473,7 +1537,7 @@ export const peopleQueryResult: { people: RecordGqlConnection } = { currencyCode: '', }, xLink: { - __typename: 'Link', + __typename: 'Links', primaryLinkLabel: '', primaryLinkUrl: '', }, diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-23/0-23-migrate-domain-to-links.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-23/0-23-migrate-domain-to-links.command.ts new file mode 100644 index 000000000..daabf671d --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-23/0-23-migrate-domain-to-links.command.ts @@ -0,0 +1,302 @@ +import { Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; + +import chalk from 'chalk'; +import { Command, CommandRunner, Option } from 'nest-commander'; +import { QueryRunner, Repository } from 'typeorm'; + +import { TypeORMService } from 'src/database/typeorm/typeorm.service'; +import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; +import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; +import { + FieldMetadataEntity, + FieldMetadataType, +} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/field-metadata.service'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { WorkspaceStatusService } from 'src/engine/workspace-manager/workspace-status/services/workspace-status.service'; +import { COMPANY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; +import { ViewService } from 'src/modules/view/services/view.service'; +import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity'; + +interface MigrateDomainNameFromTextToLinksCommandOptions { + workspaceId?: string; +} + +@Command({ + name: 'migrate-0.23:migrate-domain-standard-field-to-links', + description: + 'Migrating field domainName of deprecated type TEXT to type LINKS', +}) +export class MigrateDomainNameFromTextToLinksCommand extends CommandRunner { + private readonly logger = new Logger( + MigrateDomainNameFromTextToLinksCommand.name, + ); + constructor( + @InjectRepository(FieldMetadataEntity, 'metadata') + private readonly fieldMetadataRepository: Repository, + private readonly fieldMetadataService: FieldMetadataService, + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, + private readonly typeORMService: TypeORMService, + private readonly dataSourceService: DataSourceService, + private readonly workspaceStatusService: WorkspaceStatusService, + private readonly viewService: ViewService, + ) { + super(); + } + + @Option({ + flags: '-w, --workspace-id [workspace_id]', + description: + 'workspace id. Command runs on all active workspaces if not provided', + required: false, + }) + parseWorkspaceId(value: string): string { + return value; + } + + async run( + _passedParam: string[], + options: MigrateDomainNameFromTextToLinksCommandOptions, + ): Promise { + this.logger.log( + 'Running command to migrate standard field domainName from text to Link', + ); + let workspaceIds: string[] = []; + + if (options.workspaceId) { + workspaceIds = [options.workspaceId]; + } else { + const activeWorkspaceIds = + await this.workspaceStatusService.getActiveWorkspaceIds(); + + workspaceIds = activeWorkspaceIds; + } + + if (!workspaceIds.length) { + this.logger.log(chalk.yellow('No workspace found')); + + return; + } else { + this.logger.log( + chalk.green(`Running command on ${workspaceIds.length} workspaces`), + ); + } + + for (const workspaceId of workspaceIds) { + this.logger.log(`Running command for workspace ${workspaceId}`); + try { + const dataSourceMetadata = + await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceId( + workspaceId, + ); + + if (!dataSourceMetadata) { + throw new Error( + `Could not find dataSourceMetadata for workspace ${workspaceId}`, + ); + } + + const workspaceDataSource = + await this.typeORMService.connectToDataSource(dataSourceMetadata); + + if (!workspaceDataSource) { + throw new Error( + `Could not connect to dataSource for workspace ${workspaceId}`, + ); + } + + const domainNameField = await this.fieldMetadataRepository.findOneBy({ + workspaceId, + standardId: COMPANY_STANDARD_FIELD_IDS.domainName, + }); + + if (!domainNameField) { + throw new Error('Could not find domainName field'); + } + + if (domainNameField.type === FieldMetadataType.LINKS) { + this.logger.log( + `Field domainName is already of type LINKS, skipping migration.`, + ); + continue; + } + + this.logger.log(`Attempting to migrate domainName field.`); + + const workspaceQueryRunner = workspaceDataSource.createQueryRunner(); + + await workspaceQueryRunner.connect(); + + const fieldName = domainNameField.name; + const { id: _id, ...domainNameFieldWithoutId } = domainNameField; + + try { + const tmpNewDomainLinksField = + await this.fieldMetadataService.createOne({ + ...domainNameFieldWithoutId, + type: FieldMetadataType.LINKS, + name: `${fieldName}Tmp`, + defaultValue: { + primaryLinkUrl: domainNameField.defaultValue, + secondaryLinks: null, + primaryLinkLabel: "''", + }, + } satisfies CreateFieldInput); + + // Migrate data from domainName to primaryLinkUrl + await this.migrateDataWithinCompanyTable({ + sourceColumnName: `${domainNameField.name}`, + targetColumnName: `${tmpNewDomainLinksField.name}PrimaryLinkUrl`, + workspaceQueryRunner, + dataSourceMetadata, + }); + + // Duplicate initial domainName text field's views behaviour for new domainName field + await this.viewService.removeFieldFromViews({ + workspaceId: workspaceId, + fieldId: tmpNewDomainLinksField.id, + }); + + const viewFieldRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + 'viewField', + ); + const viewFieldsWithDeprecatedField = await viewFieldRepository.find({ + where: { + fieldMetadataId: domainNameField.id, + isVisible: true, + }, + }); + + await this.viewService.addFieldToViews({ + workspaceId: workspaceId, + fieldId: tmpNewDomainLinksField.id, + viewsIds: viewFieldsWithDeprecatedField + .filter((viewField) => viewField.viewId !== null) + .map((viewField) => viewField.viewId as string), + positions: viewFieldsWithDeprecatedField.reduce( + (acc, viewField) => { + if (!viewField.viewId) { + return acc; + } + acc[viewField.viewId] = viewField.position; + + return acc; + }, + [], + ), + size: 150, + }); + + // Delete initial domainName text field + await this.fieldMetadataService.deleteOneField( + { id: domainNameField.id }, + workspaceId, + ); + + // Rename temporary domainName links field + await this.fieldMetadataService.updateOne(tmpNewDomainLinksField.id, { + id: tmpNewDomainLinksField.id, + workspaceId: tmpNewDomainLinksField.workspaceId, + name: `${fieldName}`, + isCustom: false, + }); + + this.logger.log(`Migration of domainName done!`); + } catch (error) { + this.logger.log( + `Failed to migrate domainName ${domainNameField.id}, rolling back.`, + ); + + // Re-create initial field if it was deleted + const initialField = + await this.fieldMetadataService.findOneWithinWorkspace( + workspaceId, + { + where: { + name: `${domainNameField.name}`, + objectMetadataId: domainNameField.objectMetadataId, + }, + }, + ); + + const tmpNewDomainLinksField = + await this.fieldMetadataService.findOneWithinWorkspace( + workspaceId, + { + where: { + name: `${domainNameField.name}Tmp`, + objectMetadataId: domainNameField.objectMetadataId, + }, + }, + ); + + if (!initialField) { + this.logger.log(`Re-creating initial domainName field`); + const restoredField = await this.fieldMetadataService.createOne({ + ...domainNameField, + }); + + if (tmpNewDomainLinksField) { + this.logger.log(`Restoring data in domainName`); + await this.migrateDataWithinCompanyTable({ + sourceColumnName: `${tmpNewDomainLinksField.name}PrimaryLinkLabel`, + targetColumnName: `${restoredField.name}PrimaryLinkLabel`, + workspaceQueryRunner, + dataSourceMetadata, + }); + + await this.migrateDataWithinCompanyTable({ + sourceColumnName: `${tmpNewDomainLinksField.name}PrimaryLinkUrl`, + targetColumnName: `${restoredField.name}PrimaryLinkUrl`, + workspaceQueryRunner, + dataSourceMetadata, + }); + } else { + this.logger.log( + `Failed to restore data in domainName field ${domainNameField.id}`, + ); + } + } + + if (tmpNewDomainLinksField) { + await this.fieldMetadataService.deleteOneField( + { id: tmpNewDomainLinksField.id }, + workspaceId, + ); + } + } finally { + await workspaceQueryRunner.release(); + } + } catch (error) { + this.logger.log( + chalk.red( + `Running command on workspace ${workspaceId} failed with error: ${error}`, + ), + ); + continue; + } + + this.logger.log(chalk.green(`Command completed!`)); + } + } + + private async migrateDataWithinCompanyTable({ + sourceColumnName, + targetColumnName, + workspaceQueryRunner, + dataSourceMetadata, + }: { + sourceColumnName: string; + targetColumnName: string; + workspaceQueryRunner: QueryRunner; + dataSourceMetadata: DataSourceEntity; + }) { + await workspaceQueryRunner.query( + `UPDATE "${dataSourceMetadata.schema}"."company" SET "${targetColumnName}" = CASE WHEN "${sourceColumnName}" LIKE 'http%' THEN "${sourceColumnName}" ELSE 'https://' || "${sourceColumnName}" END;`, + ); + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-23/0-23-upgrade-version.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-23/0-23-upgrade-version.command.ts index 2f118802e..2057f9fee 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-23/0-23-upgrade-version.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-23/0-23-upgrade-version.command.ts @@ -1,5 +1,6 @@ import { Command, CommandRunner, Option } from 'nest-commander'; +import { MigrateDomainNameFromTextToLinksCommand } from 'src/database/commands/upgrade-version/0-23/0-23-migrate-domain-to-links.command'; import { MigrateLinkFieldsToLinksCommand } from 'src/database/commands/upgrade-version/0-23/0-23-migrate-link-fields-to-links.command'; import { MigrateMessageChannelSyncStatusEnumCommand } from 'src/database/commands/upgrade-version/0-23/0-23-migrate-message-channel-sync-status-enum.command'; @@ -14,6 +15,7 @@ interface Options { export class UpgradeTo0_23Command extends CommandRunner { constructor( private readonly migrateLinkFieldsToLinks: MigrateLinkFieldsToLinksCommand, + private readonly migrateDomainNameFromTextToLinks: MigrateDomainNameFromTextToLinksCommand, private readonly migrateMessageChannelSyncStatusEnumCommand: MigrateMessageChannelSyncStatusEnumCommand, ) { super(); @@ -31,6 +33,7 @@ export class UpgradeTo0_23Command extends CommandRunner { async run(_passedParam: string[], options: Options): Promise { await this.migrateLinkFieldsToLinks.run(_passedParam, options); + await this.migrateDomainNameFromTextToLinks.run(_passedParam, options); await this.migrateMessageChannelSyncStatusEnumCommand.run( _passedParam, options, diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-23/0-23-upgrade-version.module.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-23/0-23-upgrade-version.module.ts index 01abd69f9..cba2f6872 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-23/0-23-upgrade-version.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-23/0-23-upgrade-version.module.ts @@ -1,6 +1,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { MigrateDomainNameFromTextToLinksCommand } from 'src/database/commands/upgrade-version/0-23/0-23-migrate-domain-to-links.command'; import { MigrateLinkFieldsToLinksCommand } from 'src/database/commands/upgrade-version/0-23/0-23-migrate-link-fields-to-links.command'; import { MigrateMessageChannelSyncStatusEnumCommand } from 'src/database/commands/upgrade-version/0-23/0-23-migrate-message-channel-sync-status-enum.command'; import { UpgradeTo0_23Command } from 'src/database/commands/upgrade-version/0-23/0-23-upgrade-version.command'; @@ -28,6 +29,7 @@ import { ViewModule } from 'src/modules/view/view.module'; ], providers: [ MigrateLinkFieldsToLinksCommand, + MigrateDomainNameFromTextToLinksCommand, MigrateMessageChannelSyncStatusEnumCommand, UpgradeTo0_23Command, ], diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/companies.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/companies.ts index 0d552b95e..8a556f924 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/companies.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/companies.ts @@ -28,7 +28,7 @@ export const seedCompanies = async ( .into(`${schemaName}.${tableName}`, [ 'id', 'name', - 'domainName', + 'domainNamePrimaryLinkUrl', 'addressAddressStreet1', 'addressAddressStreet2', 'addressAddressCity', @@ -42,7 +42,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.LINKEDIN, name: 'Linkedin', - domainName: 'linkedin.com', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://linkedin.com' }, addressAddressStreet1: 'Eutaw Street', addressAddressStreet2: null, addressAddressCity: 'Dublin', @@ -54,7 +54,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.FACEBOOK, name: 'Facebook', - domainName: 'facebook.com', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://facebook.com' }, addressAddressStreet1: null, addressAddressStreet2: null, addressAddressCity: null, @@ -66,7 +66,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.QONTO, name: 'Qonto', - domainName: 'qonto.com', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://qonto.com' }, addressAddressStreet1: '18 rue de navarrin', addressAddressStreet2: null, addressAddressCity: 'Paris', @@ -78,7 +78,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.MICROSOFT, name: 'Microsoft', - domainName: 'microsoft.com', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://microsoft.com' }, addressAddressStreet1: null, addressAddressStreet2: null, addressAddressCity: null, @@ -90,7 +90,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.AIRBNB, name: 'Airbnb', - domainName: 'airbnb.com', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://airbnb.com' }, addressAddressStreet1: '888 Brannan St', addressAddressStreet2: null, addressAddressCity: 'San Francisco', @@ -102,7 +102,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.GOOGLE, name: 'Google', - domainName: 'google.com', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://google.com' }, addressAddressStreet1: '760 Market St', addressAddressStreet2: 'Floor 10', addressAddressCity: 'San Francisco', @@ -114,7 +114,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.NETFLIX, name: 'Netflix', - domainName: 'netflix.com', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://netflix.com' }, addressAddressStreet1: '2300 Harrison St', addressAddressStreet2: null, addressAddressCity: 'San Francisco', @@ -126,7 +126,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.LIBEO, name: 'Libeo', - domainName: 'libeo.io', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://libeo.io' }, addressAddressStreet1: null, addressAddressStreet2: null, addressAddressCity: null, @@ -138,7 +138,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.CLAAP, name: 'Claap', - domainName: 'claap.io', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://claap.io' }, addressAddressStreet1: null, addressAddressStreet2: null, addressAddressCity: null, @@ -150,7 +150,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.HASURA, name: 'Hasura', - domainName: 'hasura.io', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://hasura.io' }, addressAddressStreet1: null, addressAddressStreet2: null, addressAddressCity: null, @@ -162,7 +162,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.WEWORK, name: 'Wework', - domainName: 'wework.com', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://wework.com' }, addressAddressStreet1: null, addressAddressStreet2: null, addressAddressCity: null, @@ -174,7 +174,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.SAMSUNG, name: 'Samsung', - domainName: 'samsung.com', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://samsung.com' }, addressAddressStreet1: null, addressAddressStreet2: null, addressAddressCity: null, @@ -186,7 +186,7 @@ export const seedCompanies = async ( { id: DEV_SEED_COMPANY_IDS.ALGOLIA, name: 'Algolia', - domainName: 'algolia.com', + domainNamePrimaryLinkUrl: { primarlyLinkUrl: 'https://algolia.com' }, addressAddressStreet1: null, addressAddressStreet2: null, addressAddressCity: null, diff --git a/packages/twenty-server/src/engine/core-modules/quick-actions/quick-actions.service.ts b/packages/twenty-server/src/engine/core-modules/quick-actions/quick-actions.service.ts index 9f7e51e01..63789e57b 100644 --- a/packages/twenty-server/src/engine/core-modules/quick-actions/quick-actions.service.ts +++ b/packages/twenty-server/src/engine/core-modules/quick-actions/quick-actions.service.ts @@ -8,7 +8,9 @@ import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metad import { stringifyWithoutKeyQuote } from 'src/engine/api/graphql/workspace-query-builder/utils/stringify-without-key-quote.util'; import { WorkspaceQueryRunnerService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service'; import { IntelligenceService } from 'src/engine/core-modules/quick-actions/intelligence.service'; +import { getCompanyNameFromDomainName } from 'src/modules/contact-creation-manager/utils/get-company-name-from-domain-name.util'; import { capitalize } from 'src/utils/capitalize'; +import { getCompanyDomainName } from 'src/utils/getCompanyDomainName'; import { isWorkEmail } from 'src/utils/is-work-email'; @Injectable() @@ -159,7 +161,7 @@ export class QuickActionsService { } const enrichedData = await this.intelligenceService.enrichCompany( - company.domainName, + getCompanyNameFromDomainName(getCompanyDomainName(company)), ); await this.workspaceQueryRunnunerService.execute( diff --git a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts index cfbb7a964..64505b9c5 100644 --- a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts +++ b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts @@ -1,11 +1,12 @@ import { FieldMetadataDefaultValue } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface'; import { FieldMetadataOptions } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-options.interface'; +import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { generateDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/generate-default-value'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage'; import { TypedReflect } from 'src/utils/typed-reflect'; -import { generateDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/generate-default-value'; export interface WorkspaceFieldOptions< T extends FieldMetadataType | 'default', @@ -17,6 +18,7 @@ export interface WorkspaceFieldOptions< icon?: string; defaultValue?: FieldMetadataDefaultValue; options?: FieldMetadataOptions; + settings?: FieldMetadataSettings; } export function WorkspaceField( diff --git a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/company.ts b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/company.ts index f1cea39e3..2a46316c5 100644 --- a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/company.ts +++ b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/company.ts @@ -11,7 +11,7 @@ export const companyPrefillDemoData = async ( .insert() .into(`${schemaName}.company`, [ 'name', - 'domainName', + 'domainNamePrimaryLinkUrl', 'addressAddressCity', 'employees', 'linkedinLinkPrimaryLinkUrl', diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/company.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/company.ts index 6061cef07..88df7879f 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/company.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/company.ts @@ -9,7 +9,7 @@ export const companyPrefillData = async ( .insert() .into(`${schemaName}.company`, [ 'name', - 'domainName', + 'domainNamePrimaryLinkUrl', 'addressAddressStreet1', 'addressAddressStreet2', 'addressAddressCity', @@ -23,7 +23,7 @@ export const companyPrefillData = async ( .values([ { name: 'Airbnb', - domainName: 'airbnb.com', + domainNamePrimaryLinkUrl: 'https://airbnb.com', addressAddressStreet1: '888 Brannan St', addressAddressStreet2: null, addressAddressCity: 'San Francisco', @@ -35,7 +35,7 @@ export const companyPrefillData = async ( }, { name: 'Qonto', - domainName: 'qonto.com', + domainNamePrimaryLinkUrl: 'https://qonto.com', addressAddressStreet1: '18 rue de navarrin', addressAddressStreet2: null, addressAddressCity: 'Paris', @@ -47,7 +47,7 @@ export const companyPrefillData = async ( }, { name: 'Stripe', - domainName: 'stripe.com', + domainNamePrimaryLinkUrl: 'https://stripe.com', addressAddressStreet1: 'Eutaw Street', addressAddressStreet2: null, addressAddressCity: 'Dublin', @@ -59,7 +59,7 @@ export const companyPrefillData = async ( }, { name: 'Figma', - domainName: 'figma.com', + domainNamePrimaryLinkUrl: 'https://figma.com', addressAddressStreet1: '760 Market St', addressAddressStreet2: 'Floor 10', addressAddressCity: 'San Francisco', @@ -71,7 +71,7 @@ export const companyPrefillData = async ( }, { name: 'Notion', - domainName: 'notion.com', + domainNamePrimaryLinkUrl: 'https://notion.com', addressAddressStreet1: '2300 Harrison St', addressAddressStreet2: null, addressAddressCity: 'San Francisco', diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts index fa109828b..2c7eb944a 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts @@ -51,10 +51,15 @@ export class SyncWorkspaceMetadataCommand extends CommandRunner { ); } + let count = 1; + const errorsDuringSync: string[] = []; for (const workspaceId of workspaceIds) { - this.logger.log(`Running workspace sync for workspace: ${workspaceId}`); + this.logger.log( + `Running workspace sync for workspace: ${workspaceId} (${count} out of ${workspaceIds.length})`, + ); + count++; try { const issues = await this.workspaceHealthService.healthCheck(workspaceId); diff --git a/packages/twenty-server/src/modules/company/repositories/company.repository.ts b/packages/twenty-server/src/modules/company/repositories/company.repository.ts index 471db6770..39aeeb5d4 100644 --- a/packages/twenty-server/src/modules/company/repositories/company.repository.ts +++ b/packages/twenty-server/src/modules/company/repositories/company.repository.ts @@ -20,6 +20,7 @@ export class CompanyRepository { public async getExistingCompaniesByDomainNames( domainNames: string[], workspaceId: string, + companyDomainNameColumnName: string, transactionManager?: EntityManager, ): Promise<{ id: string; domainName: string }[]> { const dataSourceSchema = @@ -27,7 +28,7 @@ export class CompanyRepository { const existingCompanies = await this.workspaceDataSourceService.executeRawQuery( - `SELECT id, "domainName" FROM ${dataSourceSchema}.company WHERE "domainName" = ANY($1)`, + `SELECT id, "${companyDomainNameColumnName}" AS "domainName" FROM ${dataSourceSchema}.company WHERE REGEXP_REPLACE("${companyDomainNameColumnName}", '^https?://', '') = ANY($1)`, [domainNames], workspaceId, transactionManager, @@ -56,6 +57,7 @@ export class CompanyRepository { public async createCompany( workspaceId: string, companyToCreate: CompanyToCreate, + companyDomainNameColumnName, transactionManager?: EntityManager, ): Promise { const dataSourceSchema = @@ -67,11 +69,11 @@ export class CompanyRepository { ); await this.workspaceDataSourceService.executeRawQuery( - `INSERT INTO ${dataSourceSchema}.company (id, "domainName", name, "addressAddressCity", position) + `INSERT INTO ${dataSourceSchema}.company (id, "${companyDomainNameColumnName}", name, "addressAddressCity", position) VALUES ($1, $2, $3, $4, $5)`, [ companyToCreate.id, - companyToCreate.domainName, + 'https://' + companyToCreate.domainName, companyToCreate.name ?? '', companyToCreate.city ?? '', lastCompanyPosition + 1, diff --git a/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts b/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts index 179e68c12..4d4610aab 100644 --- a/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts +++ b/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts @@ -48,7 +48,7 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceField({ standardId: COMPANY_STANDARD_FIELD_IDS.domainName, - type: FieldMetadataType.TEXT, + type: FieldMetadataType.LINKS, label: 'Domain Name', description: 'The company website URL. We use this url to fetch the company icon', diff --git a/packages/twenty-server/src/modules/contact-creation-manager/contact-creation-manager.module.ts b/packages/twenty-server/src/modules/contact-creation-manager/contact-creation-manager.module.ts index fdb994efa..40b0bb3e9 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/contact-creation-manager.module.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/contact-creation-manager.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module'; import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; @@ -24,6 +25,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta WorkspaceDataSourceModule, TypeOrmModule.forFeature([FeatureFlagEntity], 'core'), TypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'), + TypeOrmModule.forFeature([FieldMetadataEntity], 'metadata'), ], providers: [ CreateCompanyService, diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts index 14a984cb2..fc912fb5a 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts @@ -7,8 +7,13 @@ import compact from 'lodash.compact'; import { EntityManager, Repository } from 'typeorm'; import { ObjectRecordCreateEvent } from 'src/engine/integrations/event-emitter/types/object-record-create.event'; +import { + FieldMetadataEntity, + FieldMetadataType, +} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; +import { COMPANY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; import { CONTACTS_CREATION_BATCH_SIZE } from 'src/modules/contact-creation-manager/constants/contacts-creation-batch-size.constant'; @@ -36,12 +41,15 @@ export class CreateCompanyAndContactService { private readonly eventEmitter: EventEmitter2, @InjectRepository(ObjectMetadataEntity, 'metadata') private readonly objectMetadataRepository: Repository, + @InjectRepository(FieldMetadataEntity, 'metadata') + private readonly fieldMetadataRepository: Repository, ) {} private async createCompaniesAndPeople( connectedAccount: ConnectedAccountWorkspaceEntity, contactsToCreate: Contact[], workspaceId: string, + companyDomainNameColumnName: string, transactionManager?: EntityManager, ): Promise { if (!contactsToCreate || contactsToCreate.length === 0) { @@ -103,6 +111,7 @@ export class CreateCompanyAndContactService { const companiesObject = await this.createCompaniesService.createCompanies( domainNamesToCreate, workspaceId, + companyDomainNameColumnName, transactionManager, ); @@ -146,11 +155,24 @@ export class CreateCompanyAndContactService { throw new Error('Object metadata not found'); } + const domainNameFieldMetadata = await this.fieldMetadataRepository.findOne({ + where: { + workspaceId: workspaceId, + standardId: COMPANY_STANDARD_FIELD_IDS.domainName, + }, + }); + + const companyDomainNameColumnName = + domainNameFieldMetadata?.type === FieldMetadataType.LINKS + ? 'domainNamePrimaryLinkUrl' + : 'domainName'; + for (const contactsBatch of contactsBatches) { const createdPeople = await this.createCompaniesAndPeople( connectedAccount, contactsBatch, workspaceId, + companyDomainNameColumnName, ); for (const createdPerson of createdPeople) { diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts index 266bdad12..8d35f64d6 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts @@ -8,6 +8,7 @@ import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repos import { CompanyRepository } from 'src/modules/company/repositories/company.repository'; import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/company.workspace-entity'; import { getCompanyNameFromDomainName } from 'src/modules/contact-creation-manager/utils/get-company-name-from-domain-name.util'; +import { getCompanyDomainName } from 'src/utils/getCompanyDomainName'; @Injectable() export class CreateCompanyService { private readonly httpService: AxiosInstance; @@ -24,6 +25,7 @@ export class CreateCompanyService { async createCompanies( domainNames: string[], workspaceId: string, + companyDomainNameColumnName: string, transactionManager?: EntityManager, ): Promise<{ [domainName: string]: string; @@ -38,6 +40,7 @@ export class CreateCompanyService { await this.companyRepository.getExistingCompaniesByDomainNames( uniqueDomainNames, workspaceId, + companyDomainNameColumnName, transactionManager, ); @@ -61,7 +64,7 @@ export class CreateCompanyService { (domainName) => !existingCompanies.some( (company: { domainName: string }) => - company.domainName === domainName, + getCompanyDomainName(company) === domainName, ), ); @@ -69,6 +72,7 @@ export class CreateCompanyService { companiesObject[domainName] = await this.createCompany( domainName, workspaceId, + companyDomainNameColumnName, transactionManager, ); } @@ -79,6 +83,7 @@ export class CreateCompanyService { private async createCompany( domainName: string, workspaceId: string, + companyDomainNameColumnName, transactionManager?: EntityManager, ): Promise { const companyId = v4(); @@ -93,6 +98,7 @@ export class CreateCompanyService { name, city, }, + companyDomainNameColumnName, transactionManager, ); diff --git a/packages/twenty-server/src/modules/view/services/view.service.ts b/packages/twenty-server/src/modules/view/services/view.service.ts index 4228ea127..ed34f8e81 100644 --- a/packages/twenty-server/src/modules/view/services/view.service.ts +++ b/packages/twenty-server/src/modules/view/services/view.service.ts @@ -17,6 +17,7 @@ export class ViewService { fieldId, viewsIds, positions, + size, }: { workspaceId: string; fieldId: string; @@ -24,6 +25,7 @@ export class ViewService { positions?: { [key: string]: number; }[]; + size?: number; }) { const viewFieldRepository = await this.twentyORMGlobalManager.getRepositoryForWorkspace( @@ -51,6 +53,7 @@ export class ViewService { fieldMetadataId: fieldId, isVisible: true, ...(isDefined(position) && { position: position }), + ...(isDefined(size) && { size: size }), }); await viewFieldRepository.save(newViewField); diff --git a/packages/twenty-server/src/utils/getCompanyDomainName.ts b/packages/twenty-server/src/utils/getCompanyDomainName.ts new file mode 100644 index 000000000..21bd2105d --- /dev/null +++ b/packages/twenty-server/src/utils/getCompanyDomainName.ts @@ -0,0 +1,13 @@ +import { isDefined } from 'class-validator'; + +// temporary, to remove once domainName has been fully migrated to Links type +export const getCompanyDomainName = (company: any) => { + if (!isDefined(company.domainName)) { + return company.domainName; + } + if (typeof company.domainName === 'string') { + return company.domainName; + } else { + return company.domainName.primaryLinkUrl; + } +};