-
Notifications
You must be signed in to change notification settings - Fork 5.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
fix(core): Fix workflow tagging failure due to unique constraint check #8505
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -166,10 +166,7 @@ export class WorkflowService { | |
); | ||
|
||
if (tagIds && !config.getEnv('workflowTagsDisabled')) { | ||
await this.workflowTagMappingRepository.delete({ workflowId }); | ||
await this.workflowTagMappingRepository.insert( | ||
tagIds.map((tagId) => ({ tagId, workflowId })), | ||
); | ||
await this.workflowTagMappingRepository.overwriteTaggings(workflowId, tagIds); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we update the tags in the same transaction as we update the workflow itself? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, same for workflow history - I was looking to avoid a bigger change than for this issue. Let me know otherwise. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
|
||
if (workflow.versionId !== shared.workflow.versionId) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import Container from 'typedi'; | ||
|
||
import * as testDb from './shared/testDb'; | ||
import { WorkflowTagMappingRepository } from '@/databases/repositories/workflowTagMapping.repository'; | ||
import { createWorkflow } from './shared/db/workflows'; | ||
import { TagRepository } from '@/databases/repositories/tag.repository'; | ||
|
||
describe('WorkflowTagMappingRepository', () => { | ||
let taggingRepository: WorkflowTagMappingRepository; | ||
let tagRepository: TagRepository; | ||
|
||
beforeAll(async () => { | ||
await testDb.init(); | ||
|
||
taggingRepository = Container.get(WorkflowTagMappingRepository); | ||
tagRepository = Container.get(TagRepository); | ||
}); | ||
|
||
afterEach(async () => { | ||
await testDb.truncate(['WorkflowTagMapping', 'Workflow', 'Tag']); | ||
}); | ||
|
||
afterAll(async () => { | ||
await testDb.terminate(); | ||
}); | ||
|
||
describe('overwriteTaggings', () => { | ||
test('should overwrite taggings in a workflow', async () => { | ||
const workflow = await createWorkflow(); | ||
|
||
const oldTags = await tagRepository.save( | ||
['tag1', 'tag2'].map((name) => tagRepository.create({ name })), | ||
); | ||
|
||
const oldTaggings = oldTags.map((tag) => | ||
taggingRepository.create({ | ||
tagId: tag.id, | ||
workflowId: workflow.id, | ||
}), | ||
); | ||
|
||
await taggingRepository.save(oldTaggings); | ||
|
||
const newTags = await tagRepository.save( | ||
['tag3', 'tag4'].map((name) => tagRepository.create({ name })), | ||
); | ||
|
||
await taggingRepository.overwriteTaggings( | ||
workflow.id, | ||
newTags.map((t) => t.id), | ||
); | ||
|
||
const taggings = await taggingRepository.findBy({ workflowId: workflow.id }); | ||
|
||
expect(taggings).toHaveLength(2); | ||
|
||
const [firstNewTag, secondNewTag] = newTags; | ||
|
||
expect(taggings).toEqual( | ||
expect.arrayContaining([ | ||
expect.objectContaining({ tagId: firstNewTag.id, workflowId: workflow.id }), | ||
expect.objectContaining({ tagId: secondNewTag.id, workflowId: workflow.id }), | ||
]), | ||
); | ||
}); | ||
|
||
test('should delete taggings if no tags are provided', async () => { | ||
const workflow = await createWorkflow(); | ||
|
||
const oldTags = await tagRepository.save( | ||
['tag1', 'tag2'].map((name) => tagRepository.create({ name })), | ||
); | ||
|
||
const oldTaggings = oldTags.map((tag) => | ||
taggingRepository.create({ | ||
tagId: tag.id, | ||
workflowId: workflow.id, | ||
}), | ||
); | ||
|
||
await taggingRepository.save(oldTaggings); | ||
|
||
await taggingRepository.overwriteTaggings(workflow.id, []); | ||
|
||
const taggings = await taggingRepository.findBy({ workflowId: workflow.id }); | ||
|
||
expect(taggings).toHaveLength(0); | ||
}); | ||
}); | ||
}); |
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 wonder if we change this to
await this.delete({ workflowId });
, would that run the query then outside the transaction somehow?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 should check the source but from quick looking at the docs, all the examples use the transactional entity manager syntax.
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.
we know for certain that on sqlite this will definitely execute inside a transaction. we can always test this manually on postgres to confirm.
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.
Using this syntax:
Runs both queries inside a transaction in Postgres:
Why do we prefer this syntax over
tx.delete
andtx.insert
? I find this more explicit.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 that I prefer one way over the other. I'm just curious if we can make code like this generally less verbose by not having to pass the entity type in the repository for that entity. we don't have to pass
WorkflowTagMapping
in any of the non transaction calls, having to pass it in transaction calls feels like a bad API.