Skip to content
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

feat: NoValue is bot properly created the backend #9110

Merged
merged 4 commits into from
Dec 18, 2024

Conversation

magrinj
Copy link
Member

@magrinj magrinj commented Dec 17, 2024

No Value view groups wasn't properly created when we select a group by field metadata, this PR fix the issue.
Also a script is added to backfill the current view groups.

@magrinj magrinj force-pushed the feat/view-group-no-value branch from 73623fe to 8ca14e7 Compare December 18, 2024 08:55
@magrinj magrinj marked this pull request as ready for review December 18, 2024 09:14
Copy link
Contributor

@greptile-apps greptile-apps bot left a 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 proper handling of 'No Value' view groups for nullable fields, including a backfill command to fix existing data and frontend support for displaying these groups.

  • Added ViewGroupNoValueBackfillCommand in /packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-view-group-no-value-backfill.command.ts to fix existing view groups
  • Added syncNoValueViewGroup method in FieldMetadataRelatedRecordsService to manage empty value groups based on field nullability
  • Modified frontend handling in useHandleRecordGroupField to create 'No Value' groups for nullable fields
  • Updated mapViewGroupsToRecordGroupDefinitions to properly display 'No Value' columns in the UI
  • Added transaction support in FieldMetadataRelatedRecordsService for data consistency

7 file(s) reviewed, 6 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines +77 to +78
!existingGroupKeys.has(`${fieldMetadataItem.id}:`) &&
fieldMetadataItem.isNullable === true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The check ${fieldMetadataItem.id}: looks for an exact match with a colon at the end, but line 54 creates keys with ${group.fieldMetadataId}:${group.fieldValue}. This means the check will never match existing empty value groups.

id: v4(),
fieldValue: '',
isVisible: true,
position: fieldMetadataItem.options.length,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using options.length as position could cause conflicts if options were deleted and re-added, since existing groups may already use these positions. Consider using max position + 1 instead.

Comment on lines +44 to 46
if (!selectedOption && selectFieldMetadataItem.isNullable === false) {
return null;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: This check could prevent valid 'No Value' groups from being created if fieldValue is empty string or null

Comment on lines 58 to 60
// We're assuming for now that all viewGroups have the same fieldMetadataId
const viewGroup = view.viewGroups?.[0];
const fieldMetadataId = viewGroup?.fieldMetadataId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Dangerous assumption that all viewGroups share same fieldMetadataId. Should verify this assumption holds or handle cases where it doesn't.

Comment on lines +74 to +78
await this.fieldMetadataRelatedRecordsService.syncNoValueViewGroup(
fieldMetadata,
view,
viewGroupRepository,
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Missing transaction manager parameter. Should pass transaction to ensure atomic operations.

Comment on lines +49 to +51
const views = await viewRepository.find({
relations: ['viewGroups'],
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider batching view fetches for better performance with large datasets.

transactionManager?: EntityManager,
): Promise<void> {
const noValueGroup = view.viewGroups.find(
(group) => group.fieldValue === '',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity, why did we decide that no value would be an empty string rather than a null value?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's something we can think of, as this field was not nullable, I go that way on the first implementation few PR's ago when we want to make it movable.
cc @charlesBochet, what do you think ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by project convention we don't want to have two states representing a null value.

For a string: '' and null would both represent a null string. ==> We have chosen to only rely on ''
For a JSON: {}, '', [] and null would all represent the null case. ==> We have chosen to only rely on null
For an array: [] and null ==> we use []

@@ -150,7 +178,7 @@ export class FieldMetadataRelatedRecordsService {
'view',
);

return await viewRepository.find({
return viewRepository.find({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we not need await anymore here ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we're directly returning a promise so await is not needed as it doesn't change anything

Copy link
Member

@Weiko Weiko Dec 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ijreilly @magrinj we had issues in the past where people were updating the code from

return viewRepository.find({
...

to

const views = viewRepository.find({
... // doing some new stuff between fetching the views and returning it
return views;

forgetting to put back the await and breaking the code so I usually put it anyway. Not a strong requirement though, we don't have consensus yet but just a FYI.
The only advantage I see with removing the await is that it delegates the decision to await to the caller but in practice we don't benefit from this that often

…-40/0-40-view-group-no-value-backfill.command.ts

Co-authored-by: Marie <[email protected]>
@magrinj magrinj merged commit 3b48920 into main Dec 18, 2024
22 checks passed
@magrinj magrinj deleted the feat/view-group-no-value branch December 18, 2024 11:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants