-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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: Synchronize deletions when pulling from source control #12170
Changes from all commits
c93c018
e0f7d40
1bb030a
4198543
f46d07e
b398a56
1a5fc09
2dd1459
a04f7c0
ade8362
a8e1e94
181918e
d8ad3ab
c33b873
aaa2d2d
d0d9211
1f5d8c2
09115e0
1273e31
9b9c452
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 |
---|---|---|
|
@@ -9,9 +9,11 @@ import { readFile as fsReadFile } from 'node:fs/promises'; | |
import path from 'path'; | ||
|
||
import { ActiveWorkflowManager } from '@/active-workflow-manager'; | ||
import { CredentialsService } from '@/credentials/credentials.service'; | ||
import type { Project } from '@/databases/entities/project'; | ||
import { SharedCredentials } from '@/databases/entities/shared-credentials'; | ||
import type { TagEntity } from '@/databases/entities/tag-entity'; | ||
import type { User } from '@/databases/entities/user'; | ||
import type { Variables } from '@/databases/entities/variables'; | ||
import type { WorkflowTagMapping } from '@/databases/entities/workflow-tag-mapping'; | ||
import { CredentialsRepository } from '@/databases/repositories/credentials.repository'; | ||
|
@@ -25,7 +27,9 @@ import { WorkflowTagMappingRepository } from '@/databases/repositories/workflow- | |
import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; | ||
import type { IWorkflowToImport } from '@/interfaces'; | ||
import { isUniqueConstraintError } from '@/response-helper'; | ||
import { TagService } from '@/services/tag.service'; | ||
import { assertNever } from '@/utils'; | ||
import { WorkflowService } from '@/workflows/workflow.service'; | ||
|
||
import { | ||
SOURCE_CONTROL_CREDENTIAL_EXPORT_FOLDER, | ||
|
@@ -62,6 +66,9 @@ export class SourceControlImportService { | |
private readonly variablesRepository: VariablesRepository, | ||
private readonly workflowRepository: WorkflowRepository, | ||
private readonly workflowTagMappingRepository: WorkflowTagMappingRepository, | ||
private readonly workflowService: WorkflowService, | ||
private readonly credentialsService: CredentialsService, | ||
private readonly tagService: TagService, | ||
instanceSettings: InstanceSettings, | ||
) { | ||
this.gitFolder = path.join(instanceSettings.n8nFolder, SOURCE_CONTROL_GIT_FOLDER); | ||
|
@@ -500,6 +507,30 @@ export class SourceControlImportService { | |
return result; | ||
} | ||
|
||
async deleteWorkflowsNotInWorkfolder(user: User, candidates: SourceControlledFile[]) { | ||
for (const candidate of candidates) { | ||
await this.workflowService.delete(user, candidate.id); | ||
} | ||
} | ||
|
||
async deleteCredentialsNotInWorkfolder(user: User, candidates: SourceControlledFile[]) { | ||
for (const candidate of candidates) { | ||
await this.credentialsService.delete(user, candidate.id); | ||
} | ||
} | ||
|
||
async deleteVariablesNotInWorkfolder(candidates: SourceControlledFile[]) { | ||
for (const candidate of candidates) { | ||
await this.variablesService.delete(candidate.id); | ||
} | ||
} | ||
|
||
async deleteTagsNotInWorkfolder(candidates: SourceControlledFile[]) { | ||
for (const candidate of candidates) { | ||
await this.tagService.delete(candidate.id); | ||
} | ||
} | ||
|
||
Comment on lines
+510
to
+533
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. these currently fire multiple read and then delete queries. maybe we should add a 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. I can do that. Honestly I avoided this because I don't think this is a hot path and being correct was more important to me than being efficient and those functions will always work and there is no chance that they get out of sync with the What I could do is create a If you approve I'll do that, let's just agree on what we want before we start to work on it. |
||
private async findOrCreateOwnerProject(owner: ResourceOwner): Promise<Project | null> { | ||
if (typeof owner === 'string' || owner.type === 'personal') { | ||
const email = typeof owner === 'string' ? owner : owner.personalEmail; | ||
|
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.
The check for if a user has access to a credential belongs in the controller (or in a middleware) IMO.
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.
Oh, I've moved that to the services as much as possible with RBAC, because
credService.remove(user, ids)
from any place and be certain that it will check if the user is allowed to do that action or not, otherwise we put the onus of not creating security flows on the caller, not the calleen8n/packages/cli/src/workflows/workflow.service.ts
Lines 275 to 282 in 1f5d8c2
That said, I can change it, if we already have a consensus about this.