-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Fix unique standard field #16371
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
Fix unique standard field #16371
Conversation
| return { | ||
| ...FIELD_METADATA_UPDATE_INDEX_SIDE_EFFECT, | ||
| flatIndexMetadatasToDelete: uniqueIndexToDelete | ||
| ? [uniqueIndexToDelete] | ||
| : [], | ||
| status: 'success', | ||
| result: { | ||
| ...FIELD_METADATA_UPDATE_INDEX_SIDE_EFFECT, | ||
| flatIndexMetadatasToDelete: uniqueIndexToDelete | ||
| ? [uniqueIndexToDelete] | ||
| : [], | ||
| }, | ||
| }; | ||
| } | ||
| const updatedIndexes = recomputeIndexOnFlatFieldMetadataNameUpdate({ |
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.
Bug: Validation for deleting unique indexes fails when applicationId is null for legacy indexes.
Severity: CRITICAL | Confidence: High
🔍 Detailed Analysis
The validation logic at uniqueIndexToDelete.applicationId !== workspaceCustomApplicationId incorrectly evaluates to true when uniqueIndexToDelete.applicationId is null. This occurs for legacy unique indexes because applicationId is nullable and was not populated by the 1-13-rename-unique-index.command.ts migration. This prevents legitimate field updates that attempt to remove uniqueness constraints, causing the operation to fail with the error: "Cannot delete unique index that have not been created by the workspace custom application".
💡 Suggested Fix
Modify the validation to explicitly handle null applicationId values, either by allowing deletion for null values or by checking isDefined(uniqueIndexToDelete.applicationId) before comparison.
🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location:
packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/handle-index-changes-during-field-update.util.ts#L152-L182
Potential issue: The validation logic at `uniqueIndexToDelete.applicationId !==
workspaceCustomApplicationId` incorrectly evaluates to `true` when
`uniqueIndexToDelete.applicationId` is `null`. This occurs for legacy unique indexes
because `applicationId` is nullable and was not populated by the
`1-13-rename-unique-index.command.ts` migration. This prevents legitimate field updates
that attempt to remove uniqueness constraints, causing the operation to fail with the
error: "Cannot delete unique index that have not been created by the workspace custom
application".
Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 5856349
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.
This is true, not all entities have the applicationId set already
We might be checking the isCustom as a fallback assertion
Greptile OverviewGreptile SummaryThis PR successfully fixes the bug where standard fields in custom objects could not be set as unique. The fix involves three main changes: Core Logic Updates
Index Naming Standardization
Testing
The implementation correctly distinguishes between standard indexes (created by the platform) and custom indexes (created by users), preventing users from breaking platform constraints while allowing flexibility for custom objects. Confidence Score: 4/5
Important Files ChangedFile Analysis
Sequence DiagramsequenceDiagram
participant User
participant GraphQL
participant FieldMetadataService
participant UpdateUtil as fromUpdateFieldInputToFlatFieldMetadata
participant SideEffectUtil as handleFlatFieldMetadataUpdateSideEffect
participant IndexUtil as handleIndexChangesDuringFieldUpdate
participant MigrationService as WorkspaceMigrationService
participant Database
User->>GraphQL: updateOneField(isUnique: true)
GraphQL->>FieldMetadataService: updateOneField()
FieldMetadataService->>FieldMetadataService: Fetch existing metadata from cache
FieldMetadataService->>UpdateUtil: fromUpdateFieldInputToFlatFieldMetadata()
UpdateUtil->>UpdateUtil: Extract and sanitize input
UpdateUtil->>UpdateUtil: Check editable properties (isUnique now allowed for standard fields)
UpdateUtil->>UpdateUtil: computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate()
UpdateUtil->>SideEffectUtil: handleFlatFieldMetadataUpdateSideEffect()
SideEffectUtil->>IndexUtil: handleIndexChangesDuringFieldUpdate()
alt isUnique changed to true
IndexUtil->>IndexUtil: Check if index exists
alt No existing index
IndexUtil->>IndexUtil: generateIndexForFlatFieldMetadata()
IndexUtil->>IndexUtil: Generate name with UNIQUE prefix (v2)
IndexUtil-->>SideEffectUtil: Return flatIndexMetadatasToCreate
end
else isUnique changed to false
IndexUtil->>IndexUtil: Find unique index to delete
IndexUtil->>IndexUtil: Check if index.applicationId == workspaceCustomApplicationId
alt Index from standard application
IndexUtil-->>SideEffectUtil: Return error (cannot delete standard index)
else Index from custom application
IndexUtil-->>SideEffectUtil: Return flatIndexMetadatasToDelete
end
end
SideEffectUtil-->>UpdateUtil: Return side effects with index changes
UpdateUtil-->>FieldMetadataService: Return flatFieldMetadatasToUpdate + index operations
FieldMetadataService->>MigrationService: validateBuildAndRunWorkspaceMigration()
MigrationService->>Database: Create/delete indexes
MigrationService->>Database: Update field metadata
Database-->>MigrationService: Success
MigrationService-->>FieldMetadataService: Success
FieldMetadataService->>FieldMetadataService: Recompute cache
FieldMetadataService-->>GraphQL: Return updated field metadata
GraphQL-->>User: Field updated with isUnique constraint
|
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.
21 files reviewed, 1 comment
| { | ||
| code: FieldMetadataExceptionCode.INVALID_FIELD_INPUT, | ||
| message: | ||
| 'Cannot delete unique index that have not been created by the workspace custom application', |
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.
syntax: Grammar: "have not been created" should be "has not been created"
| 'Cannot delete unique index that have not been created by the workspace custom application', | |
| 'Cannot delete unique index that has not been created by the workspace custom application', |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/handle-index-changes-during-field-update.util.ts
Line: 165:165
Comment:
**syntax:** Grammar: "have not been created" should be "has not been created"
```suggestion
'Cannot delete unique index that has not been created by the workspace custom application',
```
How can I resolve this? If you propose a fix, please make it concise.|
🚀 Preview Environment Ready! Your preview environment is available at: http://bore.pub:16740 This environment will automatically shut down when the PR is closed or after 5 hours. |
prastoin
left a comment
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.
Good job ! We might wanna update the frontend to also be comparing the index applicationId to the workspaceCustomApplicationId in order to disable dyanmically the input instead of checking the isCustom boolean
That's polishing
| const uniqueIndexes = await this.indexMetadataRepository.find({ | ||
| where: { | ||
| workspaceId, | ||
| isUnique: true, | ||
| }, | ||
| relations: ['objectMetadata', 'indexFieldMetadatas.fieldMetadata'], | ||
| }); | ||
|
|
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.
Suggestion: We could use workspaceCacheService directly here, might be costy to re-construction field relation from maps though TBD
...erver/src/database/commands/upgrade-version-command/1-13/1-13-rename-unique-index.command.ts
Outdated
Show resolved
Hide resolved
...erver/src/database/commands/upgrade-version-command/1-13/1-13-rename-unique-index.command.ts
Outdated
Show resolved
Hide resolved
| 'label', | ||
| 'options', | ||
| 'settings', | ||
| 'isUnique', // not editable for standard fields with standard unique constraint |
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.
At least from the api metadata
| return { | ||
| ...FIELD_METADATA_UPDATE_INDEX_SIDE_EFFECT, | ||
| flatIndexMetadatasToDelete: uniqueIndexToDelete | ||
| ? [uniqueIndexToDelete] | ||
| : [], | ||
| status: 'success', | ||
| result: { | ||
| ...FIELD_METADATA_UPDATE_INDEX_SIDE_EFFECT, | ||
| flatIndexMetadatasToDelete: uniqueIndexToDelete | ||
| ? [uniqueIndexToDelete] | ||
| : [], | ||
| }, | ||
| }; | ||
| } | ||
| const updatedIndexes = recomputeIndexOnFlatFieldMetadataNameUpdate({ |
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.
This is true, not all entities have the applicationId set already
We might be checking the isCustom as a fallback assertion
packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field-index.decorator.ts
Show resolved
Hide resolved
| ); | ||
| }); | ||
|
|
||
| describe('Standard field with standard unique index update should fail on isUnique change', () => { |
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.
neat
...data/suites/field-metadata/successful-update-one-standard-field-metadata.integration-spec.ts
Show resolved
Hide resolved
| expect(data.updateOneField.isUnique).toBe(true); | ||
| }); | ||
|
|
||
| it('should set isUnique back to false on standard field', async () => { |
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.
Remark: That's a bit of a test leakage, I would rather handle that in an after jest hook
Because if you only run the first test the isUnique value is not updated restored which could have an impact on other tests
...twenty-server/test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util.ts
Outdated
Show resolved
Hide resolved
|
Hey @etiennejouan! After you've done the QA of your Pull Request, you can mark it as done here. Thank you! |
Fixes #15925