-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Phone country code unique #backend #9035
base: main
Are you sure you want to change the base?
Conversation
…country-code-unique
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
This PR adds functionality to handle phone country codes and calling codes separately, addressing the issue where multiple countries can share the same calling code (e.g., +1 for US and Canada).
- Added new
primaryPhoneCallingCode
field across the system to store international dialing codes (e.g., '+33'), whileprimaryPhoneCountryCode
now stores ISO country codes (e.g., 'FR') - Created migration command
PhoneCallingCodeCommand
that converts existing calling codes to proper country codes and backfills the new calling code field - Added validation and default value handling for the new calling code field in metadata definitions and DTOs
- Updated GraphQL queries, OpenAPI specs, and mock data to support the new phone number structure
- Ensured migration runs before metadata sync to maintain data consistency during upgrade
13 file(s) reviewed, 10 comment(s)
Edit PR Review Bot Settings | Greptile
packages/twenty-server/src/database/commands/database-command.module.ts
Outdated
Show resolved
Hide resolved
.../twenty-server/src/database/commands/upgrade-version/0-40/0-40-phone-calling-code.command.ts
Outdated
Show resolved
Hide resolved
.../twenty-server/src/database/commands/upgrade-version/0-40/0-40-phone-calling-code.command.ts
Show resolved
Hide resolved
.../twenty-server/src/database/commands/upgrade-version/0-40/0-40-phone-calling-code.command.ts
Show resolved
Hide resolved
packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts
Show resolved
Hide resolved
packages/twenty-server/src/database/typeorm-seeds/metadata/fieldsMetadata.ts
Outdated
Show resolved
Hide resolved
packages/twenty-server/src/database/typeorm-seeds/metadata/fieldsMetadata.ts
Show resolved
Hide resolved
...ages/twenty-server/src/engine/core-modules/open-api/utils/__tests__/components.utils.spec.ts
Show resolved
Hide resolved
packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts
Show resolved
Hide resolved
This comment was marked as spam.
This comment was marked as spam.
where: { | ||
workspaceId, | ||
type: FieldMetadataType.PHONES, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can load objectMetadata as a relation here to avoid making N queries in the loop below
|
||
for (const phoneFieldMetadata of phonesFieldMetadata) { | ||
if ( | ||
isDefined(phoneFieldMetadata) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be simplified
if (isDefined(phoneFieldMetadata?.name))
isDefined(phoneFieldMetadata) && | ||
isDefined(phoneFieldMetadata.name) | ||
) { | ||
const [objectMetadata] = await this.objectMetadataRepository.find({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned above, once the relation is loaded you can directly do
phoneFieldMetadata.objectMetadata
to retrieve the objectMetadata
`P1 Step 1 - Let's find the "nameSingular" of this objectMetadata: ${objectMetadata.nameSingular || 'not found'}`, | ||
); | ||
|
||
if (!objectMetadata || !objectMetadata.nameSingular) continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!objectMetadata || !objectMetadata.nameSingular) continue; | |
if (!objectMetadata?.nameSingular) continue; |
Although you probably won't need this check once you'll load the object from field
workspaceId: workspaceId, | ||
isNullable: true, | ||
defaultValue: "''", | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
}, | |
} satisfies Partial<FieldMetadataEntity>, |
return getCountries().includes(str as CountryCode); | ||
}; | ||
|
||
export const countryCodeToCallingCode = (countryCode: string): string => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const countryCodeToCallingCode = (countryCode: string): string => {
if (!countryCode || !isStrCountryCodeGuard(countryCode)) {
return `+${DEFAULT_PHONE_CALLING_CODE}`;
}
const callingCode = getCountryCallingCode(countryCode);
return callingCode ? `+${callingCode}` : `+${DEFAULT_PHONE_CALLING_CODE}`;
};
Can we use if blocks in general? I think it's more readable this way
@@ -60,22 +59,23 @@ export const PhonesFieldInput = ({ | |||
|
|||
const phones = createPhonesFromFieldValue(fieldValue); | |||
|
|||
const defaultCallingCode = | |||
const defaultCountry = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused, why are we mixing calling code and country code there?
@@ -37,12 +41,13 @@ export const SettingsDataModelFieldPhonesForm = ({ | |||
.sort((a, b) => a.countryName.localeCompare(b.countryName)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const countries = [
{ label: 'No country', value: '' },
...useCountries()
.sort((a, b) => a.countryName.localeCompare(b.countryName))
.map((country) => ({
label: `${country.countryName} (+${country.callingCode})`,
value: country.countryCode,
})),
];
then you don't need the unshift below. Wdyt?
@@ -73,6 +78,11 @@ export const SettingsDataModelFieldPhonesForm = ({ | |||
...value, | |||
primaryPhoneCountryCode: | |||
applySimpleQuotesToString(newPhoneCountryCode), | |||
primaryPhoneCallingCode: applySimpleQuotesToString( | |||
countryCodeToCallingCode( | |||
newPhoneCountryCode as CountryCode, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure this type assertion is bringing much value
const records = await repository.find(); | ||
|
||
for (const record of records) { | ||
// this.logger.log( `P1 Step 2 - obj.nameSingular: ${objectMetadata.nameSingular} (objId: ${objectMetadata.id}) - record.id: ${record.id} - phoneFieldMetadata.name: ${phoneFieldMetadata.name}` ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a few comments! Also I think it would be nice to implement dryRun in your commands so we can safely test them 👍
fix #8775