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

refactor(core): Move WorkflowHooks to core, improve type-safety, and add tests (no-changelog) #12364

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

netroy
Copy link
Member

@netroy netroy commented Dec 24, 2024

Summary

This PR

  1. Renames WorkflowHooks to ExecutionHooks
  2. moves it from n8n-workflow to n8n-core
  3. Creates a new ExecutionHooksFactory class, and moves all execution hooks code out of workflow-execute-additional-data
  4. makes all of these hooks type-safe
  5. adds tests for the vast majority of this code.

Related Linear tickets, Github issues, and Community forum posts

Review / Merge checklist

  • PR title and summary are descriptive. (conventions)
  • Docs updated or follow-up ticket created.
  • Tests included.
  • PR Labeled with release/backport (if the PR is an urgent fix that needs to be backported)

@n8n-assistant n8n-assistant bot added core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team node/improvement New feature or request labels Dec 24, 2024
@netroy netroy force-pushed the refactor-execution-hooks branch from da7898e to 8018023 Compare December 25, 2024 09:21
@netroy netroy marked this pull request as ready for review December 30, 2024 16:33
@netroy netroy changed the title refactor(core): Move WorkflowHooks to core, improve type-safety, and add tests refactor(core): Move WorkflowHooks to core, improve type-safety, and add tests (no-changelog) Dec 30, 2024
@netroy netroy force-pushed the refactor-execution-hooks branch from 0ac5410 to f3cbdaf Compare December 30, 2024 16:41
@netroy netroy requested a review from ivov January 2, 2025 11:57
Copy link
Contributor

@ivov ivov left a comment

Choose a reason for hiding this comment

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

I'll come back later to finish reviewing and do some testing.

@@ -136,14 +142,12 @@ export class WorkflowRunner {
try {
await this.permissionChecker.check(workflowId, nodes);
} catch (error) {
const executionHooksFactory = Container.get(ExecutionHooksFactory);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we not injecting ExecutionHooksFactory in the constructor? Same below.

Copy link
Member Author

Choose a reason for hiding this comment

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

cyclic dependency. we needs a some serious rearchitecting/refactoring of this code to break that cycles.

this.activeExecutions.resolveResponsePromise(executionId, response);
},
];
additionalData.hooks.addHook('sendResponse', async (response) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make addHook accept sync and async functions? For less noise. Applies in a lot of other places as well.

Suggested change
additionalData.hooks.addHook('sendResponse', async (response) => {
additionalData.hooks.addHook('sendResponse', (response) => {

// TODO: For realtime jobs should probably also not do retry or not retry if they are older than x seconds.
// Check if they get retried by default and how often.
let job: Job;
let hooks: WorkflowHooks;
let lifecycleHooks: ExecutionHooks;
Copy link
Contributor

Choose a reason for hiding this comment

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

See note on naming at execution-hooks.ts

Comment on lines +40 to +45
declare module 'n8n-workflow' {
interface IWorkflowExecuteAdditionalData {
hooks?: ExecutionHooks;
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Come back later to check if this is needed.

@@ -46,26 +46,20 @@ export class TriggersAndPollers {

// Add the manual trigger response which resolves when the first time data got emitted
triggerResponse!.manualTriggerResponse = new Promise((resolve, reject) => {
const hooks = additionalData.hooks!;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we assert() instead of !?

Copy link
Member Author

Choose a reason for hiding this comment

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

we can. but I actually prefer to fix the typing, since these should always be available. The issue is that we've always hacked execution engine to do credential testing, and to keep the that legacy credential testing mechanism, we need to keep this optional.
Maybe we should redo credential testing first? 🤔

return hooks;
}

private addPreExecuteHooks(hooks: ExecutionHooks) {
Copy link
Contributor

@ivov ivov Jan 3, 2025

Choose a reason for hiding this comment

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

It's called PreExecute but adds a nodeExecuteAfter callback? Can we find a better name for this?

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried, but the only solution I came up was to break this up method into separate methods, and name those appropriately. I'd prefer to do that in another PR.

});
}

private addEventHooks(hooks: ExecutionHooks) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Named like this I'd expect to see all events (i.e. points in execution lifecycle) but the important one workflowExecuteAfter is missing here.

});
}

/** Returns hook functions to push data to Editor-UI */
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/** Returns hook functions to push data to Editor-UI */
/** Add hook callbacks to push live updates to the frontend. */

}
}

addHook<Hook extends keyof RegisteredHooks>(
Copy link
Contributor

@ivov ivov Jan 3, 2025

Choose a reason for hiding this comment

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

Suggestion - In my mind, the hook is the spot in the lifecycle where we run one or more registered callbacks.

Suggested change
addHook<Hook extends keyof RegisteredHooks>(
addCallback<Hook extends keyof RegisteredHooks>(

Comment on lines +157 to +158
/** Returns hook functions to save workflow execution and call error workflow */
private addSavingHooks(hooks: ExecutionHooks, isWorker: boolean) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We'll need much more work until hooks are organized in a way that makes sense, but until we get there maybe we can use more accurate names, even rather long.

Suggested change
/** Returns hook functions to save workflow execution and call error workflow */
private addSavingHooks(hooks: ExecutionHooks, isWorker: boolean) {
private addWorkflowExecuteAfterCallback(hooks: ExecutionHooks, isWorker: boolean) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team node/improvement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants