jbielick/faktory_worker_node

View on GitHub
src/mutation.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import { encode } from "./utils";
import { JobType } from "./job";
import { Client } from "./client";

const MUTATE = "MUTATE";

/**
 * @private
 */
enum Verb {
  CLEAR = "clear",
  KILL = "kill",
  DISCARD = "discard",
  REQUEUE = "requeue",
}

/**
 * @private
 */
enum Target {
  RETRIES = "retries",
  SCHEDULED = "scheduled",
  DEAD = "dead",
}

export const SCHEDULED = Target.SCHEDULED;
export const RETRIES = Target.RETRIES;
export const DEAD = Target.DEAD;

export type Filter = {
  jobtype?: JobType;
  pattern?: string;
  jids?: Array<string>;
  regexp?: string;
};

type Command = {
  cmd: Verb;
  filter: Filter;
  target: Target;
};

/**
 * A wrapper for the [Mutate API](https://github.com/contribsys/faktory/wiki/Mutate-API)
 *
 * A low-level data management API to script certain repairs or migrations.
 *
 * !!! Please be warned: MUTATE commands can be slow and/or resource intensive.
 * **They should not be used as part of your application logic.**
 */
export class Mutation {
  client: Client;
  target: Target;
  filter: Filter;
  cmd: Verb;

  /**
   * @param {Client} client
   */
  constructor(client: Client) {
    this.client = client;
    this.filter = {};
  }

  /**
   * Filters the affected jobs by a jobtype string.
   * Use this to ensure you're only affecting a single jobtype if applicable.
   * Can be chained.
   *
   * Note: jobtype and other filters do not apply for the *clear* command.
   *
   * @param {string} type jobtype fiter for operation
   * @example
   * client.dead.ofType('SendEmail').discard();
   */
  ofType(jobtype: JobType): Mutation {
    if (typeof jobtype !== "string") {
      throw new Error("jobtype given to ofType must be a string");
    }
    this.filter.jobtype = jobtype;
    return this;
  }

  /**
   * Filters the affected jobs by one or more job ids. This is much more
   * efficient when only one jid is provided. Can be chained.
   *
   * Note: jobtype and other filters do not apply for the *clear* command.
   *
   * @param  {...string} jids job ids to target for the operation
   * @example
   * await client.retries.withJids('1234').requeue();
   */
  withJids(...jids: string[] | [string[]]): Mutation {
    let ids: string[];
    if (Array.isArray(jids[0])) {
      ids = jids[0];
    } else {
      ids = <string[]>jids;
    }
    this.filter.jids = ids;
    return this;
  }

  /**
   * Filters the MUTATE selection to jobs matching a Redis SCAN pattern.
   * Can be chained.
   *
   * Note the regexp filter scans the entire job payload and can be tricky to
   * get right, for instance you'll probably need * on both sides. The regexp
   * filter option is passed to Redis's SCAN command directly, read the SCAN
   * documentation for further details.
   * https://redis.io/commands/scan
   *
   * @param {string} pattern redis SCAN pattern to target jobs for the operation
   * @example
   * await client.retries.matching("*uid:12345*").kill();
   */
  matching(pattern: string): Mutation {
    if (typeof pattern !== "string") {
      throw new Error(
        `
Argument given to matching() must be a redis SCAN compatible pattern string,
other object types cannot be translated.
See the Redis SCAN documentation for pattern matching examples.
https://redis.io/commands/scan
      `.trim()
      );
    }
    this.filter.regexp = pattern;
    return this;
  }

  /**
   * @private
   */
  private toJSON(): Command {
    return {
      cmd: this.cmd,
      target: this.target,
      filter: this.filter,
    };
  }

  /**
   * Executes a *clear* mutation. This clears the
   * set entirely **and any filtering added does not apply**.
   */
  clear(): PromiseLike<string> {
    this.cmd = Verb.CLEAR;
    return this.send();
  }

  /**
   * Executes a *kill* mutation. Jobs that are killed are sent to the dead set.
   */
  kill(): PromiseLike<string> {
    this.cmd = Verb.KILL;
    return this.send();
  }

  /**
   * Executes a *discard* mutation. Jobs that are discarded are permanently deleted.
   */
  discard(): PromiseLike<string> {
    this.cmd = Verb.DISCARD;
    return this.send();
  }

  /**
   * Executes a *requeue* mutation. Jobs that are requeued are sent back to their
   * original queue for processing.
   */
  requeue(): PromiseLike<string> {
    this.cmd = Verb.REQUEUE;
    return this.send();
  }

  /**
   * @private
   */
  send(): PromiseLike<string> {
    return this.client.sendWithAssert([MUTATE, encode(this.toJSON())], "OK");
  }
}