mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 04:47:29 -08:00
refactor(core): Decouple workflow created, saved, deleted events from internal hooks (no-changelog) (#10264)
This commit is contained in:
parent
efb71dd9ad
commit
d8688bd463
|
@ -10,22 +10,15 @@ import type {
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { TelemetryHelpers } from 'n8n-workflow';
|
import { TelemetryHelpers } from 'n8n-workflow';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import { N8N_VERSION } from '@/constants';
|
import { N8N_VERSION } from '@/constants';
|
||||||
import type { AuthProviderType } from '@db/entities/AuthIdentity';
|
import type { AuthProviderType } from '@db/entities/AuthIdentity';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
|
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
|
||||||
import { determineFinalExecutionStatus } from '@/executionLifecycleHooks/shared/sharedHookFunctions';
|
import { determineFinalExecutionStatus } from '@/executionLifecycleHooks/shared/sharedHookFunctions';
|
||||||
import type {
|
import type { ITelemetryUserDeletionData, IExecutionTrackProperties } from '@/Interfaces';
|
||||||
ITelemetryUserDeletionData,
|
|
||||||
IWorkflowDb,
|
|
||||||
IExecutionTrackProperties,
|
|
||||||
} from '@/Interfaces';
|
|
||||||
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
|
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
|
||||||
import { NodeTypes } from '@/NodeTypes';
|
import { NodeTypes } from '@/NodeTypes';
|
||||||
import { Telemetry } from '@/telemetry';
|
import { Telemetry } from '@/telemetry';
|
||||||
import type { Project } from '@db/entities/Project';
|
|
||||||
import { ProjectRelationRepository } from './databases/repositories/projectRelation.repository';
|
|
||||||
import { MessageEventBus } from './eventbus/MessageEventBus/MessageEventBus';
|
import { MessageEventBus } from './eventbus/MessageEventBus/MessageEventBus';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +33,6 @@ export class InternalHooks {
|
||||||
private readonly nodeTypes: NodeTypes,
|
private readonly nodeTypes: NodeTypes,
|
||||||
private readonly sharedWorkflowRepository: SharedWorkflowRepository,
|
private readonly sharedWorkflowRepository: SharedWorkflowRepository,
|
||||||
workflowStatisticsService: WorkflowStatisticsService,
|
workflowStatisticsService: WorkflowStatisticsService,
|
||||||
private readonly projectRelationRepository: ProjectRelationRepository,
|
|
||||||
// Can't use @ts-expect-error because only dev time tsconfig considers this as an error, but not build time
|
// Can't use @ts-expect-error because only dev time tsconfig considers this as an error, but not build time
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore - needed until we decouple telemetry
|
// @ts-ignore - needed until we decouple telemetry
|
||||||
|
@ -72,78 +64,6 @@ export class InternalHooks {
|
||||||
this.telemetry.track('User responded to personalization questions', personalizationSurveyData);
|
this.telemetry.track('User responded to personalization questions', personalizationSurveyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
onWorkflowCreated(
|
|
||||||
user: User,
|
|
||||||
workflow: IWorkflowBase,
|
|
||||||
project: Project,
|
|
||||||
publicApi: boolean,
|
|
||||||
): void {
|
|
||||||
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes);
|
|
||||||
|
|
||||||
this.telemetry.track('User created workflow', {
|
|
||||||
user_id: user.id,
|
|
||||||
workflow_id: workflow.id,
|
|
||||||
node_graph_string: JSON.stringify(nodeGraph),
|
|
||||||
public_api: publicApi,
|
|
||||||
project_id: project.id,
|
|
||||||
project_type: project.type,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onWorkflowDeleted(user: User, workflowId: string, publicApi: boolean): void {
|
|
||||||
this.telemetry.track('User deleted workflow', {
|
|
||||||
user_id: user.id,
|
|
||||||
workflow_id: workflowId,
|
|
||||||
public_api: publicApi,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async onWorkflowSaved(user: User, workflow: IWorkflowDb, publicApi: boolean): Promise<void> {
|
|
||||||
const isCloudDeployment = config.getEnv('deployment.type') === 'cloud';
|
|
||||||
|
|
||||||
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes, {
|
|
||||||
isCloudDeployment,
|
|
||||||
});
|
|
||||||
|
|
||||||
let userRole: 'owner' | 'sharee' | 'member' | undefined = undefined;
|
|
||||||
const role = await this.sharedWorkflowRepository.findSharingRole(user.id, workflow.id);
|
|
||||||
if (role) {
|
|
||||||
userRole = role === 'workflow:owner' ? 'owner' : 'sharee';
|
|
||||||
} else {
|
|
||||||
const workflowOwner = await this.sharedWorkflowRepository.getWorkflowOwningProject(
|
|
||||||
workflow.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (workflowOwner) {
|
|
||||||
const projectRole = await this.projectRelationRepository.findProjectRole({
|
|
||||||
userId: user.id,
|
|
||||||
projectId: workflowOwner.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (projectRole && projectRole !== 'project:personalOwner') {
|
|
||||||
userRole = 'member';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const notesCount = Object.keys(nodeGraph.notes).length;
|
|
||||||
const overlappingCount = Object.values(nodeGraph.notes).filter(
|
|
||||||
(note) => note.overlapping,
|
|
||||||
).length;
|
|
||||||
|
|
||||||
this.telemetry.track('User saved workflow', {
|
|
||||||
user_id: user.id,
|
|
||||||
workflow_id: workflow.id,
|
|
||||||
node_graph_string: JSON.stringify(nodeGraph),
|
|
||||||
notes_count_overlapping: overlappingCount,
|
|
||||||
notes_count_non_overlapping: notesCount - overlappingCount,
|
|
||||||
version_cli: N8N_VERSION,
|
|
||||||
num_tags: workflow.tags?.length ?? 0,
|
|
||||||
public_api: publicApi,
|
|
||||||
sharing_role: userRole,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line complexity
|
// eslint-disable-next-line complexity
|
||||||
async onWorkflowPostExecute(
|
async onWorkflowPostExecute(
|
||||||
_executionId: string,
|
_executionId: string,
|
||||||
|
|
|
@ -60,10 +60,12 @@ export = {
|
||||||
);
|
);
|
||||||
|
|
||||||
await Container.get(ExternalHooks).run('workflow.afterCreate', [createdWorkflow]);
|
await Container.get(ExternalHooks).run('workflow.afterCreate', [createdWorkflow]);
|
||||||
Container.get(InternalHooks).onWorkflowCreated(req.user, createdWorkflow, project, true);
|
|
||||||
Container.get(EventService).emit('workflow-created', {
|
Container.get(EventService).emit('workflow-created', {
|
||||||
workflow: createdWorkflow,
|
workflow: createdWorkflow,
|
||||||
user: req.user,
|
user: req.user,
|
||||||
|
publicApi: true,
|
||||||
|
projectId: project.id,
|
||||||
|
projectType: project.type,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.json(createdWorkflow);
|
return res.json(createdWorkflow);
|
||||||
|
@ -259,11 +261,10 @@ export = {
|
||||||
}
|
}
|
||||||
|
|
||||||
await Container.get(ExternalHooks).run('workflow.afterUpdate', [updateData]);
|
await Container.get(ExternalHooks).run('workflow.afterUpdate', [updateData]);
|
||||||
void Container.get(InternalHooks).onWorkflowSaved(req.user, updateData, true);
|
|
||||||
Container.get(EventService).emit('workflow-saved', {
|
Container.get(EventService).emit('workflow-saved', {
|
||||||
user: req.user,
|
user: req.user,
|
||||||
workflowId: updateData.id,
|
workflow: updateData,
|
||||||
workflowName: updateData.name,
|
publicApi: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.json(updatedWorkflow);
|
return res.json(updatedWorkflow);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import type { MessageEventBus } from '../MessageEventBus/MessageEventBus';
|
||||||
import type { Event } from '../event.types';
|
import type { Event } from '../event.types';
|
||||||
import { EventService } from '../event.service';
|
import { EventService } from '../event.service';
|
||||||
import type { INode, IRun, IWorkflowBase } from 'n8n-workflow';
|
import type { INode, IRun, IWorkflowBase } from 'n8n-workflow';
|
||||||
|
import type { IWorkflowDb } from '@/Interfaces';
|
||||||
|
|
||||||
describe('AuditEventRelay', () => {
|
describe('AuditEventRelay', () => {
|
||||||
const eventBus = mock<MessageEventBus>();
|
const eventBus = mock<MessageEventBus>();
|
||||||
|
@ -29,6 +30,9 @@ describe('AuditEventRelay', () => {
|
||||||
id: 'wf123',
|
id: 'wf123',
|
||||||
name: 'Test Workflow',
|
name: 'Test Workflow',
|
||||||
}),
|
}),
|
||||||
|
publicApi: false,
|
||||||
|
projectId: 'proj123',
|
||||||
|
projectType: 'personal',
|
||||||
};
|
};
|
||||||
|
|
||||||
eventService.emit('workflow-created', event);
|
eventService.emit('workflow-created', event);
|
||||||
|
@ -57,6 +61,7 @@ describe('AuditEventRelay', () => {
|
||||||
role: 'user',
|
role: 'user',
|
||||||
},
|
},
|
||||||
workflowId: 'wf789',
|
workflowId: 'wf789',
|
||||||
|
publicApi: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
eventService.emit('workflow-deleted', event);
|
eventService.emit('workflow-deleted', event);
|
||||||
|
@ -83,8 +88,8 @@ describe('AuditEventRelay', () => {
|
||||||
lastName: 'Johnson',
|
lastName: 'Johnson',
|
||||||
role: 'editor',
|
role: 'editor',
|
||||||
},
|
},
|
||||||
workflowId: 'wf101',
|
workflow: mock<IWorkflowDb>({ id: 'wf101', name: 'Updated Workflow' }),
|
||||||
workflowName: 'Updated Workflow',
|
publicApi: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
eventService.emit('workflow-saved', event);
|
eventService.emit('workflow-saved', event);
|
||||||
|
|
|
@ -86,13 +86,13 @@ export class AuditEventRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Redactable()
|
@Redactable()
|
||||||
private workflowSaved({ user, workflowId, workflowName }: Event['workflow-saved']) {
|
private workflowSaved({ user, workflow }: Event['workflow-saved']) {
|
||||||
void this.eventBus.sendAuditEvent({
|
void this.eventBus.sendAuditEvent({
|
||||||
eventName: 'n8n.audit.workflow.updated',
|
eventName: 'n8n.audit.workflow.updated',
|
||||||
payload: {
|
payload: {
|
||||||
...user,
|
...user,
|
||||||
workflowId,
|
workflowId: workflow.id,
|
||||||
workflowName,
|
workflowName: workflow.name,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -272,7 +272,7 @@ export class AuditEventRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API key
|
* Public API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Redactable()
|
@Redactable()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { AuthenticationMethod, IRun, IWorkflowBase } from 'n8n-workflow';
|
import type { AuthenticationMethod, IRun, IWorkflowBase } from 'n8n-workflow';
|
||||||
import type { IWorkflowExecutionDataProcess } from '@/Interfaces';
|
import type { IWorkflowDb, IWorkflowExecutionDataProcess } from '@/Interfaces';
|
||||||
import type { ProjectRole } from '@/databases/entities/ProjectRelation';
|
import type { ProjectRole } from '@/databases/entities/ProjectRelation';
|
||||||
import type { GlobalRole } from '@/databases/entities/User';
|
import type { GlobalRole } from '@/databases/entities/User';
|
||||||
|
|
||||||
|
@ -20,17 +20,21 @@ export type Event = {
|
||||||
'workflow-created': {
|
'workflow-created': {
|
||||||
user: UserLike;
|
user: UserLike;
|
||||||
workflow: IWorkflowBase;
|
workflow: IWorkflowBase;
|
||||||
|
publicApi: boolean;
|
||||||
|
projectId: string;
|
||||||
|
projectType: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
'workflow-deleted': {
|
'workflow-deleted': {
|
||||||
user: UserLike;
|
user: UserLike;
|
||||||
workflowId: string;
|
workflowId: string;
|
||||||
|
publicApi: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
'workflow-saved': {
|
'workflow-saved': {
|
||||||
user: UserLike;
|
user: UserLike;
|
||||||
workflowId: string;
|
workflow: IWorkflowDb;
|
||||||
workflowName: string;
|
publicApi: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
'workflow-pre-execute': {
|
'workflow-pre-execute': {
|
||||||
|
|
|
@ -8,6 +8,10 @@ import { License } from '@/License';
|
||||||
import { GlobalConfig } from '@n8n/config';
|
import { GlobalConfig } from '@n8n/config';
|
||||||
import { N8N_VERSION } from '@/constants';
|
import { N8N_VERSION } from '@/constants';
|
||||||
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
|
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
|
||||||
|
import { TelemetryHelpers } from 'n8n-workflow';
|
||||||
|
import { NodeTypes } from '@/NodeTypes';
|
||||||
|
import { SharedWorkflowRepository } from '@/databases/repositories/sharedWorkflow.repository';
|
||||||
|
import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class TelemetryEventRelay {
|
export class TelemetryEventRelay {
|
||||||
|
@ -17,6 +21,9 @@ export class TelemetryEventRelay {
|
||||||
private readonly license: License,
|
private readonly license: License,
|
||||||
private readonly globalConfig: GlobalConfig,
|
private readonly globalConfig: GlobalConfig,
|
||||||
private readonly workflowRepository: WorkflowRepository,
|
private readonly workflowRepository: WorkflowRepository,
|
||||||
|
private readonly nodeTypes: NodeTypes,
|
||||||
|
private readonly sharedWorkflowRepository: SharedWorkflowRepository,
|
||||||
|
private readonly projectRelationRepository: ProjectRelationRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
@ -101,6 +108,16 @@ export class TelemetryEventRelay {
|
||||||
this.eventService.on('login-failed-due-to-ldap-disabled', (event) => {
|
this.eventService.on('login-failed-due-to-ldap-disabled', (event) => {
|
||||||
this.loginFailedDueToLdapDisabled(event);
|
this.loginFailedDueToLdapDisabled(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.eventService.on('workflow-created', (event) => {
|
||||||
|
this.workflowCreated(event);
|
||||||
|
});
|
||||||
|
this.eventService.on('workflow-deleted', (event) => {
|
||||||
|
this.workflowDeleted(event);
|
||||||
|
});
|
||||||
|
this.eventService.on('workflow-saved', async (event) => {
|
||||||
|
await this.workflowSaved(event);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private teamProjectUpdated({ userId, role, members, projectId }: Event['team-project-updated']) {
|
private teamProjectUpdated({ userId, role, members, projectId }: Event['team-project-updated']) {
|
||||||
|
@ -431,6 +448,79 @@ export class TelemetryEventRelay {
|
||||||
this.telemetry.track('User login failed since ldap disabled', { user_ud: userId });
|
this.telemetry.track('User login failed since ldap disabled', { user_ud: userId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private workflowCreated({
|
||||||
|
user,
|
||||||
|
workflow,
|
||||||
|
publicApi,
|
||||||
|
projectId,
|
||||||
|
projectType,
|
||||||
|
}: Event['workflow-created']) {
|
||||||
|
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes);
|
||||||
|
|
||||||
|
this.telemetry.track('User created workflow', {
|
||||||
|
user_id: user.id,
|
||||||
|
workflow_id: workflow.id,
|
||||||
|
node_graph_string: JSON.stringify(nodeGraph),
|
||||||
|
public_api: publicApi,
|
||||||
|
project_id: projectId,
|
||||||
|
project_type: projectType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private workflowDeleted({ user, workflowId, publicApi }: Event['workflow-deleted']) {
|
||||||
|
this.telemetry.track('User deleted workflow', {
|
||||||
|
user_id: user.id,
|
||||||
|
workflow_id: workflowId,
|
||||||
|
public_api: publicApi,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async workflowSaved({ user, workflow, publicApi }: Event['workflow-saved']) {
|
||||||
|
const isCloudDeployment = config.getEnv('deployment.type') === 'cloud';
|
||||||
|
|
||||||
|
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes, {
|
||||||
|
isCloudDeployment,
|
||||||
|
});
|
||||||
|
|
||||||
|
let userRole: 'owner' | 'sharee' | 'member' | undefined = undefined;
|
||||||
|
const role = await this.sharedWorkflowRepository.findSharingRole(user.id, workflow.id);
|
||||||
|
if (role) {
|
||||||
|
userRole = role === 'workflow:owner' ? 'owner' : 'sharee';
|
||||||
|
} else {
|
||||||
|
const workflowOwner = await this.sharedWorkflowRepository.getWorkflowOwningProject(
|
||||||
|
workflow.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (workflowOwner) {
|
||||||
|
const projectRole = await this.projectRelationRepository.findProjectRole({
|
||||||
|
userId: user.id,
|
||||||
|
projectId: workflowOwner.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (projectRole && projectRole !== 'project:personalOwner') {
|
||||||
|
userRole = 'member';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const notesCount = Object.keys(nodeGraph.notes).length;
|
||||||
|
const overlappingCount = Object.values(nodeGraph.notes).filter(
|
||||||
|
(note) => note.overlapping,
|
||||||
|
).length;
|
||||||
|
|
||||||
|
this.telemetry.track('User saved workflow', {
|
||||||
|
user_id: user.id,
|
||||||
|
workflow_id: workflow.id,
|
||||||
|
node_graph_string: JSON.stringify(nodeGraph),
|
||||||
|
notes_count_overlapping: overlappingCount,
|
||||||
|
notes_count_non_overlapping: notesCount - overlappingCount,
|
||||||
|
version_cli: N8N_VERSION,
|
||||||
|
num_tags: workflow.tags?.length ?? 0,
|
||||||
|
public_api: publicApi,
|
||||||
|
sharing_role: userRole,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async serverStarted() {
|
private async serverStarted() {
|
||||||
const cpus = os.cpus();
|
const cpus = os.cpus();
|
||||||
const binaryDataConfig = config.getEnv('binaryDataManager');
|
const binaryDataConfig = config.getEnv('binaryDataManager');
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Container, { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
import { NodeApiError } from 'n8n-workflow';
|
import { NodeApiError } from 'n8n-workflow';
|
||||||
import pick from 'lodash/pick';
|
import pick from 'lodash/pick';
|
||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
|
@ -17,7 +17,6 @@ import { validateEntity } from '@/GenericHelpers';
|
||||||
import { ExternalHooks } from '@/ExternalHooks';
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
import { hasSharing, type ListQuery } from '@/requests';
|
import { hasSharing, type ListQuery } from '@/requests';
|
||||||
import { TagService } from '@/services/tag.service';
|
import { TagService } from '@/services/tag.service';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
|
||||||
import { OwnershipService } from '@/services/ownership.service';
|
import { OwnershipService } from '@/services/ownership.service';
|
||||||
import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee';
|
import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
|
@ -219,11 +218,10 @@ export class WorkflowService {
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.externalHooks.run('workflow.afterUpdate', [updatedWorkflow]);
|
await this.externalHooks.run('workflow.afterUpdate', [updatedWorkflow]);
|
||||||
void Container.get(InternalHooks).onWorkflowSaved(user, updatedWorkflow, false);
|
|
||||||
this.eventService.emit('workflow-saved', {
|
this.eventService.emit('workflow-saved', {
|
||||||
user,
|
user,
|
||||||
workflowId: updatedWorkflow.id,
|
workflow: updatedWorkflow,
|
||||||
workflowName: updatedWorkflow.name,
|
publicApi: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (updatedWorkflow.active) {
|
if (updatedWorkflow.active) {
|
||||||
|
@ -282,8 +280,7 @@ export class WorkflowService {
|
||||||
await this.workflowRepository.delete(workflowId);
|
await this.workflowRepository.delete(workflowId);
|
||||||
await this.binaryDataService.deleteMany(idsForDeletion);
|
await this.binaryDataService.deleteMany(idsForDeletion);
|
||||||
|
|
||||||
Container.get(InternalHooks).onWorkflowDeleted(user, workflowId, false);
|
this.eventService.emit('workflow-deleted', { user, workflowId, publicApi: false });
|
||||||
this.eventService.emit('workflow-deleted', { user, workflowId });
|
|
||||||
await this.externalHooks.run('workflow.afterDelete', [workflowId]);
|
await this.externalHooks.run('workflow.afterDelete', [workflowId]);
|
||||||
|
|
||||||
return workflow;
|
return workflow;
|
||||||
|
|
|
@ -179,8 +179,13 @@ export class WorkflowsController {
|
||||||
delete savedWorkflowWithMetaData.shared;
|
delete savedWorkflowWithMetaData.shared;
|
||||||
|
|
||||||
await this.externalHooks.run('workflow.afterCreate', [savedWorkflow]);
|
await this.externalHooks.run('workflow.afterCreate', [savedWorkflow]);
|
||||||
this.internalHooks.onWorkflowCreated(req.user, newWorkflow, project!, false);
|
this.eventService.emit('workflow-created', {
|
||||||
this.eventService.emit('workflow-created', { user: req.user, workflow: newWorkflow });
|
user: req.user,
|
||||||
|
workflow: newWorkflow,
|
||||||
|
publicApi: false,
|
||||||
|
projectId: project!.id,
|
||||||
|
projectType: project!.type,
|
||||||
|
});
|
||||||
|
|
||||||
const scopes = await this.workflowService.getWorkflowScopes(req.user, savedWorkflow.id);
|
const scopes = await this.workflowService.getWorkflowScopes(req.user, savedWorkflow.id);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue