npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ukhomeoffice/taskflow

v2.7.0

Published

Proof of concept for workflow task management

Downloads

59

Readme

taskflow

Proof of concept for workflow task management

About

taskflow is a tool for building API services to facilitate the processing of data through task-based workflows.

It creates an express router with a set of endpoints for creating and managing "cases" as they move through a workflow process.

The processing of data objects is based on the concepts of statuses, tasks, and a set of hooks which can bind functionality onto status changes or task completion.

Requirements

  • An express app
  • An authentication mechanism
  • A postgres database

Usage

In your express app:

const authentication = require('./my-authentication-middleware');
const taskflow = require('taskflow');

app.use(authentication());
app.use(taskflow({ /* options */ }));

Options

  • db - database configuration settings
    • host
    • port
    • user
    • password
    • database

Database setup

The required postgres schema can be generated in a database by calling the migrate method on an initialised taskflow instance:

const taskflow = require('taskflow');
const flow = Taskflow({
  db: {
    user: 'postgres',
    database: 'my-new-db'
  }
});
flow.migrate()
  .then(() => {
    /* database is ready to go */
  });

Alternatively, the database schema can be migrated from the command line:

taskflow migrate [options]

Where the options are as defined above. e.g. taskflow migrate --user postgres --database my-new-db

Hooks

Hooks are defined on a taskflow instance using the hook method.

Hooks should return promises, and are called after the event is written to the database.

const flow = taskflow();
flow
  .hook('create', () => {
    /* will be called when a new object is created */
  })
  .hook('status:new:old', () => {
    /* will be called when an object's status changes from "new" to "old" */
  })
  .hook('status:*:*', () => {
    /* will be called when an object's status is changed in any way */
  });

Hooks can also be set to be triggered before a change is made, by prefixing the event name with pre-. This can be useful for performing validation on input data.

const flow = taskflow();
flow
  .hook('pre-create', () => {
    /* will be called before a new object is created */
  })
  .hook('pre-status:new:old', () => {
    /* will be called when an object's status changes from "new" to "old" */
  })
  .hook('pre-status:*:*', () => {
    /* will be called when an object's status is changed in any way */
  });

Events

The following events can have hooks applied:

  • create - called when a case is created
  • update - called when the data from a case is modified
  • status:<old>:<new> - called when a case's status is modified

Arguments

Hook functions are passed a single argument which contains event details and metadata, and has attached methods for performing actions on the case object.

The event object will always have the following properties:

  • id - the id of the case object
  • event - the event that triggered the current hook - e.g. create/update
  • meta - any metadata related to the event - e.g. the user that triggered the event or any arguments passed to the event
  • status - the current status of the case
  • data - the full case data

Case mutation methods

The event object exposes the following methods:

  • setStatus(status) - updates the status property of the case
  • update(data) - updates the data of the case
  • patch(data) - patches the data of the case
  • assign(user) - assigns the case to the defined user

These methods are only available on post-event hooks, and attempting to call them on a pre-event hook will result in a warning.

Calling these methods will trigger events and related hooks, so care should be taken not to create infinite recursive loops of updating data or statuses.

If hooks cause updates to case statuses or data then any subsequent hooks will be called with the updated values.

Side-effects and downstream services

If a hook needs to perform a side-effect, such as calling an external service in response to a hook event, it is recommended to wait for the request to complete so that side-effects are only applied once the database transaction is resolved.

To do this you can wrap the side-effect in an onSettled call:

flow.hook('update', event => {
  event.onSettled(() => {
    // this is only called once all updates are complete and the database transaction is commited
  });
});

Redirecting to another case

If you wan to prevent the creation of a new case, and instead refer the client to a pre-existing case, you can call event.redirect(id | { id }) in a pre-create hook.

Decorators

Decorator functions can be defined to add additional properties to cases at read time.

Decorators take the case object as an argument, and should return a modifed case with any custom properties applied.

To define a decorator function:

const flow = taskflow();
flow
  .decorate(case => {
    return { ...case, customProperty: 'my custom property' };
  });

Decorator functions can be asynchronous, and should return promises (or be async functions).

const flow = taskflow();
flow
  .decorate(case => {
    return Database.query()
      .then(result => {
        return { ...case, customProperty: result.propertyFromDatabase };
      });
  });

Running tests

The tests are built to run against a real postgres database, so to run the unit tests you will need a databse running.

The default test configuration can be found in ./knexfile.js and can be overwritten by setting environment variables (or configuring local variables in a .env file).