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

bookshelf-history

v2.0.0

Published

Backup and restore utility for Bookshelf.js

Downloads

12

Readme

bookshelf-history

bookshelf-history is a plugin for Bookshelf.js that lets you easily handle row backup and restoration. Even automatic backup is supported.

Usage

// Just plug History in bookshelf
bookshelf.plugin(require('bookshelf-history'))

// Now create your model as usual setting the property history to true to allow
// custom methods to work properly and enable automatic backup
let User = bookshelf.Model.extend({ tableName: 'users', history: true })

Methods

  • Model.backup(resourceId, [options]) perform a manual backup on a model that has history enabled. resourceId must be the ID from the row to be backed up and options is an optional object where you can pass a transaction object as options.transacting.
  • Model.revert(resourceId, [sequence], [options]) restores a resource to a specific back-up sequence. If no sequence is provided the resource will be restored to the latest known sequence. options is an optional object where you can pass a transaction object as options.transacting.
  • Model.historyModel() returns the history model being used for query operations in the Model or false if history is disabled.

Default options

let defaults = {
  fields: {
    sequence: Integer,
    resource_id: Any,
    resource_type: String,
    metadata: Object,
    diff: Object,
    data: Object,
    patch: Boolean,
    operation: String,
    getMetadata: Function
  },
  model: bookshelf.Model.extend({ tableName: 'history' })
  autoHistory: [ 'created', 'updated' ],

}

These options can be globally overwritten when adding the plugin to bookshelf

bookshelf.plugin(require('bookshelf-history'), {
  fields: {
    sequence: 'version',
    data: 'row_content'
  },
  model: bookshelf.Model.extend({ tableName: 'backups' })
})

Or also when creating each subsequent model

let User = bookshelf.Model.extend({
  tableName: 'user',
  history: {
    autoHistory: false
  }
})

Additional Metadata

History now supports a getMetadata(model) option that will allow you to implement a function to return additional key value pairs to be stored with the history. In the example below, we use the continuation pattern, to fetch the logged in user / admin who initiated a HTTP request that is mutating the model. The data is saved as metadata in the History table.

const getNamespace = require('cls-hooked').getNamespace
const localStorage = getNamespace('app')
history: {
  getMetadata: () => {
    if (!localStorage) {
      return
    }

    return {
      author_id: localStorage.get('author_id'),
      author_type: localStorage.get('author_type')
    }
  }
}

Migration

Below is an example migration. All fields are required with the exception of created_at. You can also specify custom field names using the configuration as shown in the section above.

History also supports JSON and JSONB field types out of the box for the data field when running with PostgreSQL. With other databases the data field gets stringifyed with JSON.stringify() so make sure your data field is long enough to store all data you need.

exports.up = async (knex) => {
  await knex.schema.createTable('history', function (t) {
    t.uuid('id')
      .primary()
      .notNullable()
      .defaultTo(knex.raw('uuid_generate_v4()'))
    t.integer('sequence').notNullable()
    t.string('operation').notNullable()
    t.boolean('patch').notNullable()
    t.string('resource_type').notNullable()
    t.uuid('resource_id').notNullable()
    t.jsonb('metadata')
    t.jsonb('diff')
    t.jsonb('data')
    t.timestamp(true, true)
  })
}

exports.down = async (knex) => {
  await knex.schema.dropTable('history')
}

Bypassing backups

If you want to perform an operation inside a History enabled model but don't want to trigger an automatic backup you can make it two ways:

// Disable automatic backups entirely for the model
let User = bookshelf.Model.extend({
  tableName: 'users',
  history: {
    autoHistory: false
  }
})

// Or disable for a single operation
User.forge({
  name: 'John',
  email: '[email protected]'
})
.save(null, { history: false })

Testing

cd ./bookshelf-history
npm install

In .env place the DATABASE_URL that will be used for tests. Or you can pass it on the command line.

DATABASE_URL=postgres://postgres:postgres@localhost/bookshelf_history npm test