nestjs / schedule

Schedule module for Nest framework (node.js) ⏰

Home Page:https://nestjs.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[feature idea] a parameter to disable jobs under certain conditions

quezak opened this issue · comments

I'm submitting a...


[ ] Regression 
[ ] Bug report
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

Some of my @Interval jobs should not be enabled based on some conditions. Currently I see two ways to achieve this:

  • add a "wrapper decorator" [1] that checks the condition, and if it's true, executes the @Interval decorator. Big disadvantage: the condition is checked too soon (when the js module is imported?) -- in particular, before the app's bootstrap function in main.ts even starts running, so it's not possible to initialize some conditions before building the app, or before intializing test modules in tests.
  • manually disable these jobs in SchedulerRegistry in onModuleInit or somewhere. Doesn't have the above disadvantage, but needs to be done manually and separately from the interval method, which adds some bolierplate.

Idea:

Add another optional parameter to @Interval, say options?: { enabled?: () => boolean }.

The condition function would then be checked by the scheduler before enabling the job during application bootstrap. This solves both the issues I mentioned above.

// (maybe name could then also be moved to the options object?)

It would be even better if the condition function could use injection, e.g. for some "global" config service. That could overcomplicate things though, so it wouldn't be required.

Minimal reproduction of the problem with instructions

// ---- main.ts
// <imports>
// Problem: the code below works only if this assignment would be *at the top*
// of this file, if it's below imports, the condition in `@IntervalWhen` is
// evaluated before the assignment is executed.
process.env.TEST_JOB_ENABLED = 'false'
// <app bootstrap>

// ---- some.service.ts
class SomeService {
  @IntervalWhen(() => process.env.TEST_JOB_ENABLED, 'test_job', 10000)
  async testScheduledJob(): Promise<void> { ... }
}

// ---- [1] wrapper decorator
// version 1, simple:
function IntervalWhen(
  enabled: () => boolean,
  jobName: string,
  intervalMs: number,
): MethodDecorator {
  if (enabled()) return Interval(jobName, intervalMs);
  console.log(`job ${jobName} disabled`);
  return () => {};
}

// version 2 -- thought maybe this one will evaluate the condition later, but
// the message is also logged even before application bootstrap starts
function IntervalWhen(
  enabled: () => boolean,
  jobName: string,
  intervalMs: number,
): MethodDecorator {
  return (target, key, descriptor) => {
    if (enabled()) return Interval(jobName, intervalMs)(target, key, descriptor);
    console.log(`job ${jobName} disabled`);
    return descriptor;
  }
}

What is the motivation / use case for changing the behavior?

Example use cases:

  • a job that should be enabled only on certain app instances
  • disabling some jobs in unit/e2e tests
  • probably some more, these two are the ones I'm using

References

The nest-schedule package that I've used previously has a options?: { enabled?: boolean, ... }, but since it accepts a value, not a function, it shares the disadvantage of my "wrapper decorator" approach -- the condition is evaluated too soon.

If you need to set up intervals dynamically, you should use a "dynamic" approach https://docs.nestjs.com/techniques/task-scheduling#dynamic-intervals.