Coder Social home page Coder Social logo

nielsgl / sequelize-paper-trail Goto Github PK

View Code? Open in Web Editor NEW
94.0 8.0 69.0 1.07 MB

Sequelize plugin for tracking revision history of model instances.

License: MIT License

JavaScript 100.00%
sequelize sequelize-paper-trail papertrail revision sequelizejs sequelize-extension sequelize-library nodejs javascript mysql

sequelize-paper-trail's Introduction

Sequelize Paper Trail


Help wanted: Please try out [email protected] and give a ๐Ÿ‘/๐Ÿ‘Ž here if it works as expected.


Track changes to your models, for auditing or versioning. See how a model looked at any stage in its lifecycle, revert it to any version, or restore it after it has been destroyed. Record the user who created the version.

node-version npm-version David David

GitHub release GitHub tag GitHub commits npm-downloads

license

Table of Contents

Installation

npm install --save sequelize-paper-trail
# or with yarn:
# yarn add sequelize-paper-trail

Note: the current test suite is very limited in coverage.

Usage

Sequelize Paper Trail assumes that you already set up your Sequelize connection, for example, like this:

const Sequelize = require('sequelize');
const sequelize = new Sequelize('database', 'username', 'password');

then adding Sequelize Paper Trail is as easy as:

const PaperTrail = require('sequelize-paper-trail').init(sequelize, options);
PaperTrail.defineModels();

which loads the Paper Trail library, and the defineModels() method sets up a Revisions and RevisionHistory table.

Note: If you pass userModel option to init in order to enable user tracking, userModel should be setup before defineModels() is called.

Then for each model that you want to keep a paper trail you simply add:

Model.hasPaperTrail();

hasPaperTrail returns the hasMany association to the revisionModel so you can keep track of the association for reference later.

Example

const Sequelize = require('sequelize');
const sequelize = new Sequelize('database', 'username', 'password');

const PaperTrail = require('sequelize-paper-trail').init(sequelize, options || {});
PaperTrail.defineModels();

const User = sequelize.define('User', {
  username: Sequelize.STRING,
  birthday: Sequelize.DATE
});

User.Revisions = User.hasPaperTrail();

User Tracking

There are 2 steps to enable user tracking, ie, recording the user who created a particular revision.

  1. Enable user tracking by passing userModel option to init, with the name of the model which stores users in your application as the value.
const options = {
  /* ... */
  userModel: 'user',
};
  1. Pass the id of the user who is responsible for the database operation to sequelize-paper-trail either by sequelize options or by using continuation-local-storage.
Model.update({
  /* ... */
}, {
  userId: user.id
}).then(() {
  /* ... */
});

OR

const createNamespace = require('continuation-local-storage').createNamespace;
const session = createNamespace('my session');

session.set('userId', user.id);

Model.update({
  /* ... */
}).then(() {
  /* ... */
});

To enable continuation-local-storage set continuationNamespace in initialization options. Additionally, you may also have to call .run() or .bind() on your cls namespace, as described in the docs.

Disable logging for a single call

To not log a specific change to a revisioned object, just pass a noPaperTrail with a truthy (true, 1, ' ') value.

const instance = await Model.findOne();
instance.update({ noPaperTrail: true }).then(() {
  /* ... */
});

Options

Paper Trail supports various options that can be passed into the initialization. The following are the default options:

Default options

// Default options
const options = {
  exclude: [
    'id',
    'createdAt',
    'updatedAt',
    'deletedAt',
    'created_at',
    'updated_at',
    'deleted_at'
  ],
  revisionAttribute: 'revision',
  revisionModel: 'Revision',
  revisionChangeModel: 'RevisionChange',
  enableRevisionChangeModel: false,
  UUID: false,
  underscored: false,
  underscoredAttributes: false,
  defaultAttributes: {
    documentId: 'documentId',
    revisionId: 'revisionId'
  },
  enableCompression: false,
  enableMigration: false,
  enableStrictDiff: true,
  continuationKey: 'userId',
  belongsToUserOptions: undefined,
  metaDataFields: undefined,
  metaDataContinuationKey: 'metaData'
};

Options documentation

Option Type Default Value Description
[debug] Boolean false Enables logging to the console.
[exclude] Array ['id', 'createdAt', 'updatedAt', 'deletedAt', 'created_at', 'updated_at', 'deleted_at', [options.revisionAttribute]] Array of global attributes to exclude from the paper trail.
[revisionAttribute] String 'revision' Name of the attribute in the table that corresponds to the current revision.
[revisionModel] String 'Revision' Name of the model that keeps the revision models.
[tableName] String undefined Name of the table that keeps the revision models. Passed to Sequelize. Necessary in Sequelize 5+ when underscored is true and the table is camelCase or PascalCase.
[revisionChangeModel] String 'RevisionChange' Name of the model that tracks all the attributes that have changed during each create and update call.
[enableRevisionChangeModel] Boolean false Disable the revision change model to save space.
[UUID] Boolean false The [revisionModel] has id attribute of type UUID for postgresql.
[underscored] Boolean false The [revisionModel] and [revisionChangeModel] have 'createdAt' and 'updatedAt' attributes, by default, setting this option to true changes it to 'created_at' and 'updated_at'.
[underscoredAttributes] Boolean false The [revisionModel] has a [defaultAttribute] 'documentId', and the [revisionChangeModel] has a [defaultAttribute] 'revisionId, by default, setting this option to true changes it to 'document_id' and 'revision_id'.
[defaultAttributes] Object { documentId: 'documentId', revisionId: 'revisionId' }
[userModel] String Name of the model that stores users in your.
[enableCompression] Boolean false Compresses the revision attribute in the [revisionModel] to only the diff instead of all model attributes.
[enableMigration] Boolean false Automatically adds the [revisionAttribute] via a migration to the models that have paper trails enabled.
[enableStrictDiff] Boolean true Reports integers and strings as different, e.g. 3.14 !== '3.14'
[continuationNamespace] String Name of the name space used with the continuation-local-storage module.
[continuationKey] String 'userId' The continuation-local-storage key that contains the user id.
[belongsToUserOptions] Object undefined The options used for belongsTo between userModel and Revision model
[metaDataFields] Object undefined The keys that will be provided in the meta data object. { key: isRequired (boolean)} format. Can be used to privovide additional fields - other associations, dates, etc to the Revision model
[metaDataContinuationKey] String 'metaData' The continuation-local-storage key that contains the meta data object, from where the metaDataFields are extracted.

Limitations

  • This project does not support models with composite primary keys. You can work around using a unique index with multiple fields.

Testing

The tests are designed to run on SQLite3 in-memory tables, built from Sequelize migration files. If you want to actually generate a database file, change the storage option to a filename and run the tests.

npm test
# or with yarn:
# yarn test

Support

Please use:

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Added some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Author

ยฉ Niels van Galen Last โ€“ @nielsgl โ€“ [email protected] Distributed under the MIT license. See LICENSE for more information. https://github.com/nielsgl/sequelize-paper-trail

Thanks

This project was inspired by:

Contributors: https://github.com/nielsgl/sequelize-paper-trail/graphs/contributors

Links

sequelize-paper-trail's People

Contributors

bkoltai avatar createthis avatar dependabot[bot] avatar ekeuus avatar gauravarora avatar ksfreitas avatar mhaamann avatar mrgcohen avatar nielsgl avatar njgerner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sequelize-paper-trail's Issues

Getting syntax error from MySQL

I have followed all step which are mention for installing and using in model. when I started my server, I got following error.
Unhandled rejection SequelizeBaseError: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'JSON NOT NULL, documentId INTEGER NOT NULL, revision INTEGER NOT NULL, `crea' at line 1

I have solved that issue by just change of JSON to TEXT in following code in index file of package.
attributes = {
path: {
type: Sequelize.TEXT,
allowNull: false
},
document: {
type: Sequelize.JSON,
allowNull: false
},
diff: {
type: Sequelize.JSON,
allowNull: false
}
};

But still its giving me other error that is:
Unhandled rejection SequelizeBaseError: ER_DUP_FIELDNAME: Duplicate column name 'RevisionId'

Now I am not able solved this error. Could you please help me for that

Revision table not getting created.

I installed sequelize-paper-trail and used the docs to set it up in my project and attach to a model. With debug: true as my only option it appears to start up fine and track changes I'm making to the model through sequelize, however the Revision and RevisionChanges tables are not getting created so it fails to actually track. [email protected]

Get opt.model.name undefined

Hi,

I have tried to use the library but unfortunately I am getting an error at the step when the revision gets built. It seem that opt.model.name property is undefined in my case. Is there a specific format in which the model has to be constructed for this to work? Here is the portion of code that throws the error [TypeError: Cannot read property 'name' of undefined] in lib.js line 272

  // Build revision
  var revision = Revision.build({
    model: opt.model.name,
    document_id: instance.get("id"),
    // TODO: Hacky, but necessary to get immutable current representation
    document: currentVersion
  });

Thank you in advance for you help

db.transaction breaks paper trail context

return db.transaction(function (transaction) {
	return article.save(); // also fails if a transaction is provided in options
});

When I save something outside transaction, it works perfectly. As soon as I have multiple saves or have saves in a transaction context, the whole paper trail context is gone.

I tried with Sequelize CLS and without. Neither version worked.

Am I doing something incorrectly? Thanks in advance.

relation "revisions" does not exist

I'm getting relation "revisions" does not exist with version 2.6.1

'INSERT INTO "revisions" ("id","model","document","operation","document_id","revision","created_at","updated_at") VALUES (DEFAULT,\'User\',\'{"last_email_validation_at":"2017-07-11T20:27:30.778Z","should_update_password":false,"login_attemps":0,"terms_n_conditions_accepted_at":"2018-03-28T14:38:59.976Z","email":"[email protected]","password":"483483a29358524790249a3d219c4377","enabled":true,"first_name":"Count Dooku","last_name":"Evildoer","marketing_emails":true,"app_version":null,"last_sign_in_at":null,"phone_number":null,"latest_version":"0.0.0","reset_password_token":null,"reset_password_expires":null,"confirmation_token":null,"confirmed_at":null,"last_phone_verification_at":"2017-07-11T20:27:33.572Z","opt_in":false,"sms_authentication":true,"source_app":null,"disabled_at":null,"modified_at":null}\',\'create\',1,1,\'2019-05-02 18:39:04.060 +00:00\',\'2019-05-02 18:39:04.060 +00:00\') RETURNING *;' },
  original:
   { Error: ERROR:  relation "revisions" does not exist```

Not possible change defaultAttributes

Changing defaultAttributes has no effect. This code override the user (line 77):

  if (!options.underscoredAttributes) {
    options.underscoredAttributes = false;
    options.defaultAttributes = defaultAttributes;
  } else {
    options.defaultAttributes = helpers.toUnderscored(defaultAttributes);
  }

enableCompression breaks first insert on hasPaperTrail models

Hi,

I think i may have found a bug.
On the line 224 of index.js:

if (!instance._previousDataValues[a].dataValues && !instance.dataValues[a].dataValues)

this code runs even on the first insert on the table when compression is enable, resulting in the error:

TypeError: Cannot read property 'dataValues' of undefined

I think we r missing a simple check of instance._previousDataValues.

Support for JSONB

I don't see any of my JSONB columns being stored in the revisions. Am I doing something wrong or does paper trail not support JSONB for Postgres?

hasPaperTrail is not a function - sequelize ^3.30.4

Below code is working fine with sequelize 4 and mysql 2 but not with 3.30.4

var sequelize = new Sequelize(config);
var db = {};
forEach(function (modelPath) {
var model = sequelize.import(path.resolve(modelPath));
db[model.name] = model;
}
Object.keys(db).forEach(function (modelName) {
if (db[modelName].options.hasOwnProperty('associate')) {
db[modelName].options.associate(db);
}
});

var PaperTrail = require('sequelize-paper-trail').init(sequelize, {debug:true});

PaperTrail.defineModels({db});

db.User.Revisions = db.User.hasPaperTrail();

TypeError: Cannot read property 'current_user_request' of undefined

Seeing this error when attempting to use this.

The problem is probably with my setup as I'm using migrations and I'm not sure if I have my tables setup correctly.

I used the migration file found in the repo and also added a revision: integer column to the table I want to use paper trail on.

full error

{ TypeError: Cannot read property 'current_user_request' of undefined
    at Model.afterHook (/Users/randallmeeker/Projects/eventcontrol/node_modules/sequelize-paper-trail/lib/index.js:244:32)
    at /Users/randallmeeker/Projects/eventcontrol/node_modules/sequelize/lib/hooks.js:142:19
    at bound (domain.js:280:14)
    at runBound (domain.js:293:12)
    at tryCatcher (/Users/randallmeeker/Projects/eventcontrol/node_modules/bluebird/js/release/util.js:16:23)
    at Object.gotValue (/Users/randallmeeker/Projects/eventcontrol/node_modules/bluebird/js/release/reduce.js:155:18)
    at Object.gotAccum (/Users/randallmeeker/Projects/eventcontrol/node_modules/bluebird/js/release/reduce.js:144:25)
    at bound (domain.js:280:14)
    at Object.runBound (domain.js:293:12)
    at Object.tryCatcher (/Users/randallmeeker/Projects/eventcontrol/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/Users/randallmeeker/Projects/eventcontrol/node_modules/bluebird/js/release/promise.js:510:31)
    at Promise._settlePromise (/Users/randallmeeker/Projects/eventcontrol/node_modules/bluebird/js/release/promise.js:567:18)
    at Promise._settlePromiseCtx (/Users/randallmeeker/Projects/eventcontrol/node_modules/bluebird/js/release/promise.js:604:10)
    at Async._drainQueue (/Users/randallmeeker/Projects/eventcontrol/node_modules/bluebird/js/release/async.js:138:12)
    at Async._drainQueues (/Users/randallmeeker/Projects/eventcontrol/node_modules/bluebird/js/release/async.js:143:10)
    at Immediate.Async.drainQueues (/Users/randallmeeker/Projects/eventcontrol/node_modules/bluebird/js/release/async.js:17:14)

Discussion: Strategies for handling null deltas when updating an existing record

I was doing some testing on an API recently with Paper Trail enabled, and found that submitting the same PATCH request multiple times increments the revision number and updatedAt fields on the record on the entity table, but does not create additional entries in the Revisions table.

This feels somewhat inconsistent to me, and I was thinking perhaps it would be good to have an option that allows us to decide whether to discard Revisions entirely (essentially abort the update operation) if the delta is null, or to write a new Revision record even in the case of a null delta. The current behaviour is somewhere in between the two, and this may cause some confusion for anyone looking for the "missing" revisions in the Revisions table.

This seems to be due to the fact that beforeHook in index.js unconditionally increments the revision number of the instance being updated...

instance.set(options.revisionAttribute, (currentRevisionId || 0) + 1);
    if (delta && delta.length > 0) {
      if (!instance.context) {
        instance.context = {};
      }
      instance.context.delta = delta;
    }
    if (debug) {
      log('end of beforeHook');
    }

...but afterHook does not build and save the Revision change record if the delta is null...

if (instance.context && instance.context.delta && instance.context.delta.length > 0) {
      var Revision = sequelize.model(options.revisionModel);
      if (options.enableRevisionChangeModel) {
        var RevisionChange = sequelize.model(options.revisionChangeModel);
      }
      var delta = instance.context.delta;

      ...

      // Build revision
      var revision = Revision.build({
        model: this.name,
        documentId: instance.id,
        document: JSON.stringify(currentVersion),
        userId: ns.get(options.continuationKey) || opt[options.continuationKey]
      });
}

If we have agreement in principle on this, I'm happy to have a crack at it and submit a pull request when done. Let me know your thoughts.

Kind regards,
Rory

UserId

I added a userModel, and userId opt at update/create (show at debug) but not insert userId at Revisions.

The Revisions table has a userdbid column.

var opts = {
//underscored: true,
//underscoredAttributes: true,
//enableCompression: false,
//enableMigration: true,
userModel: 'userdb'
,debug: true
};

afterHook called
instance: roledb {
dataValues:
{ ............ },
_previousDataValues:
{ ........ },
_changed: { structureread: true, structurewrite: true, revision: true },
_modelOptions:
{ ........ },
_options:
{ ........ },
__eagerlyLoadedAssociations: [],
isNewRecord: false,
context: { delta: [ [Object], [Object] ] } }
opt: { userId: 2,
fields: [ 'structureread', 'structurewrite', 'updatedAt', 'revision' ],
defaultFields: [ 'structureread', 'structurewrite', 'updatedAt' ],
hooks: true,
validate: true }

Paper Trail not tracking Sequelize.DATE type column

Hi, I have followed the install instructions, and got this awesome lib working on my project, ๐Ÿ’ฏ
but, when I was looking in the revisions table for the data been stored for changes in date columns, I was not able to find any date change tracked by this lib =/ .
Expected behavior: Given a Sequelize model defined with a date column, when some date value of an instance of this model is updated and persisted, the sequelize-paper-trail should track the date value update.
Any help are welcome, Thanks in advance!!

Updating (dev) dependencies and tests.

The (dev)dependencies are very outdated and tests don't cover a lot of the recent added functionality. If anyone is interested in helping out please let me know ๐Ÿ†

hasPaperTrail is not a function - sequelize ^3.30.4

I am getting the above error on trying the sample code .
I believe the error is because the way we initialize sequelize models is different .
Following is my code (relevant part)

 var sequelize = new Sequelize(config);  
 var db = {};  

  forEach(function (modelPath) {  

    var model = sequelize.import(path.resolve(modelPath));  

    db[model.name] = model;  

  }
 Object.keys(db).forEach(function (modelName) {  

  if (db[modelName].options.hasOwnProperty('associate')) {  

    db[modelName].options.associate(db);  

  }  

});  

var PaperTrail = require('sequelize-paper-trail').init(sequelize, {debug:true});  

PaperTrail.defineModels({db});  

db.User.Revisions = db.User.hasPaperTrail();  

Could you suggest some solution please.
Thanks.

Syntax error on MySQL for the JSONB data type

When performing a migration I get the following error, which seems to be due to the JSONB data type not being supported by MySQL (8.0.15)

Executing (default): CREATE TABLE IF NOT EXISTS `revisions` (`id` INTEGER NOT NULL auto_increment , `model` TEXT NOT NULL, `document` JSONB NOT NULL, `operation` VARCHAR(7), `document_id` INTEGER NOT NULL, `revision` INTEGER NOT NULL, `created_at` DATETIME NOT NULL, `updated_at` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
{
    "errorMessage": "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'JSONB NOT NULL, `operation` VARCHAR(7), `document_id` INTEGER NOT NULL, `revisio' at line 1",
    "errorType": "DatabaseError",
    "stackTrace": [
         "SequelizeDatabaseError: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'JSONB NOT NULL, `operation` VARCHAR(7), `document_id` INTEGER NOT NULL, `revisio' at line 1",
        "    at Query.formatError (/usr/src/app/node_modules/sequelize/lib/dialects/mysql/query.js:244:16)",
        "    at Query.handler [as onResult] (/usr/src/app/node_modules/sequelize/lib/dialects/mysql/query.js:51:23)",
        "    at Query.execute (/usr/src/app/node_modules/mysql2/lib/commands/command.js:30:14)",
        "    at Connection.handlePacket (/usr/src/app/node_modules/mysql2/lib/connection.js:408:32)",
        "    at PacketParser.Connection.packetParser.p [as onPacket] (/usr/src/app/node_modules/mysql2/lib/connection.js:70:12)",
        "    at PacketParser.executeStart (/usr/src/app/node_modules/mysql2/lib/packet_parser.js:75:16)",
        "    at Socket.Connection.stream.on.data (/usr/src/app/node_modules/mysql2/lib/connection.js:77:25)",
        "    at emitOne (events.js:116:13)",
        "    at Socket.emit (events.js:211:7)",
        "    at addChunk (_stream_readable.js:263:12)",
        "    at readableAddChunk (_stream_readable.js:250:11)",
        "    at Socket.Readable.push (_stream_readable.js:208:10)",
        "    at TCP.onread (net.js:601:20)"
    ]
}

throw error `false has not been defined` when starting the app

i have started the app following the readme
throw new Error(modelName + ' has not been defined'); is returned when starting the app
here's the error trace:
Error: false has not been defined
at Sequelize.model (/..../node_modules/sequelize/lib/sequelize.js:649:11)
at associate (/home/..../node_modules/sequelize-paper-trail/lib/index.js:360:42)
at /..../models/index.js:45:19
at Array.forEach (native)
at Object. (/..../index.js:43:17)
at Module._compile (module.js:410:26)
at Object.Module._extensions..js (module.js:417:10)
at Module.load (module.js:344:32)
at Function.Module._load (module.js:301:12)

could not figure out the root cause.. need your support

No revision table

There is no revision table being created, nor do I see in the code that any such table should be created (even with the migration option set to true).

Did I miss something?

Requesting changes for a specific query

Is there a way to request all changes for a specific parameter or can I add additional columns to identify stuff by? I might want to add a group or project id or so.

I was unable to find it out by reading the docs. Thanks!

I went ahead and implemented a version in #63

Crash after update to 2.5.0

Crash after update to 2.5.0

exports.init = function (sequelize: Sequelize, optionsArg: array): Model { ^ SyntaxError: Unexpected token :

Merging repoes?

Hi @nielsgl

Are you interested in merging repoes? I just forked your package the other day and updated the code quite a lot. I'm not sure if I am working in the same direction as you wanted to.

New features I added:

  • Full continuation-local-storage support (solves #2)
  • MySQL support (Postgres is also still supported)
  • Option to disable the change model since it can generate a lot of unneeded data depending your use case.
  • Added a simple migration example. (solves #3)

I will be adding tests soon.

notNull Violation: Revision.documentId cannot be null

hi,

First off: great project! Really appreciated.

I'm having a bit of a trouble to get it working, though.
When I update trailed model, sequelize returns:

{ SequelizeValidationError: notNull Violation: Revision.documentId cannot be null
    at Promise.all.then (..../node_modules/sequelize/lib/instance-validator.js:77:15)

I've left options as defaults. My update query runs as expected.
What exactly is purpose of document / documentId fields?
Should I include documentId in query somewhere?

Support for upserts?

Currently, it does not look like sequelize-paper-trail supports revisions for upserts (and possibly bulkCreate too?). Would it be possible/feasible to support this, or should I manually do findOne + insert/update? I'm using MySQL.

Revision and RevisionChanges tables are not updating

index.js

"use strict";

var fs = require('fs');
var path = require('path');
var Sequelize = require('sequelize');
var env = process.env.NODE_ENV || 'development';
var config = require(path.join(__dirname, '../config/config.json'))[env];
var sequelize = new Sequelize(config.database, config.username, config.password, config);

var db = {}; 

var opts = {
	underscored: true,
	underscoredAttributes: true,
	enableCompression: false,
	enableMigration: true,
	debug: true
};

// Load Paper Trail
var PaperTrail = require('sequelize-paper-trail')(sequelize, opts);
// Setup Revision and RevisionChange models
//PaperTrail.defineModels(db);
PaperTrail.defineModels({});
  

fs.readdirSync(__dirname)
	.filter(function(file) {
		return (file.indexOf('.') !== 0) && (file !== 'index.js');
	}).forEach(function(file) {
		var model = sequelize["import"](path.join(__dirname, file));
		db[model.name] = model;
	});

Object.keys(db).forEach(function(modelName) {
	if ('associate' in db[modelName]) {
		db[modelName].associate(db);
	}
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

User.js:

"use strict";

module.exports = function(sequelize, DataTypes) {

    var User = sequelize.define('user', {
        id: {
            type: DataTypes.UUID,
            defaultValue: DataTypes.UUIDV4,
            primaryKey: true
        },
        firstName: {
            type: DataTypes.STRING
        },
        lastName: {
            type: DataTypes.STRING
        },
        dateOfJoin: {
            type: DataTypes.DATE
        },
        mobileNumber: {
            type: DataTypes.DOUBLE
        },
        address1: {
            type: DataTypes.STRING
        },
        address2: {
            type: DataTypes.STRING
        },
        city: {
            type: DataTypes.STRING
        },
        state: {
            type: DataTypes.STRING
        },
        pin: {
            type: DataTypes.STRING
        },
        emergencyContact: {
            type: DataTypes.DOUBLE
        },
        emergencyContactRelationship: {
            type: DataTypes.STRING
        },
        dateOfBirth: {
            type: DataTypes.DATE
        },
        gender: {
            type: DataTypes.ENUM('Male', 'Female')
        },
        maritalStatus: {
            type: DataTypes.BOOLEAN
        },
        email: {
            type: DataTypes.STRING,
            unique: true,
            validate: {
                isEmail: true
            }
        },
        password: {
            type: DataTypes.STRING
        },
        lastLoggedIn: {
            type: DataTypes.DATE
        },
        passwordExpiredOn: {
            type: DataTypes.DATE
        },
        accountStatus: {
            type: DataTypes.ENUM,
            values: ['CREATED', 'VERIFIED', 'ACTIVATED'],
            defaultValue: 'CREATED'
        },
        roles: {
            type: DataTypes.ARRAY(DataTypes.STRING)
        },
        preferences: {
            type: DataTypes.JSONB
        },
        reportingUserId: {
            type: DataTypes.STRING
        },
        revision: {
            type: DataTypes.INTEGER,
            defaultValue: 0
        } 
    }, {
        classMethods: {
            associate: function(models) {
                User.belongsTo(models.designation, { foreignKey: 'designationId' });
                User.belongsTo(models.department, { foreignKey: 'departmentId' });
                User.belongsTo(models.grade, { foreignKey: 'gradeId' });
                User.belongsTo(models.user_status, { foreignKey: 'statusId' });
            }
        }
    });
    
    User.hasPaperTrail();

    return User;

};

I could see Revision and RevisionChanges tables are created, but when I have updated the value on User model, I could not any see records on these tables, revision column of user model has created with value 1 and after that it never get increased. I could not find whats the problem, Am I missing anything? Also just I looked the index.js at node_modules folder of my local machine, it shows the code of release/0.4.0 branch, not from master branch

User id not populating in revisions

I have a 'User' model in my app and have the user obj passed in all the server side requests however I don't see how to populate the value of who has performed the modification in the Revision. There does not seem to be a field for it. I could not find anything in the documentation either apart from the option for specifying the user model.

npm install gives version 1.2.6, with a missing (wrong?) git hash.

Npm install is currently replying with this tarball:
https://registry.npmjs.org/sequelize-paper-trail/-/sequelize-paper-trail-1.2.6.tgz

This tarball is behind (ahead of?) latest.

Steps to reproduce:

  1. Run npm view sequelize-paper-trail
  2. See that 1.2.6 is the latest published package. This version is tied to some unknown commit hash 13aeff4c02fe9de0586b76d0dbfda0638ce47bb6. I cannot track down this hash on github.

This version is behind the latest 1.2.3/1.2.4 on github, and is missing the continuation-local-storage updates. It seems roughly equal to the code at: 762ae6a

Current workaround:

npm install [email protected]

Discussion, previousVersion or currentVersion?

Right now the module saves the currentVersion in the document attribute of RevisionModel.

Why not save the previousVersion instead?

Consider the following code example:

# Current User object in DB:
{id: 4, firstName: 'Jonn', lastName: 'Dot'}

# Update to the user:
models.user.findOne({where: {id: 4}}).then(function (userFromDB) {
  return userFromDB.update({firstName: 'JonA', lastName: 'Doe'});
});
--
# Update to the user:
models.user.findOne({where: {id: 4}}).then(function (userFromDB) {
  return userFromDB.update({firstName: 'John'});
});
--
# Update to the user:
models.user.findOne({where: {id: 4}}).then(function (userFromDB) {
  return userFromDB.update({firstName: 'JohnB'});
});
--
# Update to the user:
models.user.findOne({where: {id: 4}}).then(function (userFromDB) {
  return userFromDB.update({lastName: 'DoT'});
});

The RevisionModel now contains ('currentVersions') in the document colum:
{id: 4, firstName: 'Jonn', lastName: 'Dot', revision: 1} // Initial Create
{id: 4, firstName: 'JonA', lastName: 'Doe', revision: 2} // Update
{id: 4, firstName: 'John', revision: 3}
{id: 4, firstName: 'JohnB', revision: 4}
{id: 4, lastName: 'DoT', revision: 5}

Having identified my spelling mistake I would now like to revert the user object back when the user was called John Doe.
With the current implementation of paper-trail we would have to replay the revision log backwards until we reach revision 3.
Quite a risky task.

Instead I suggest that we instead store the previousVersion instead. Doing this the RevisionModel from the above example will end up looking like this:
{} // Initial Create
{id: 4, firstName: 'Jonn', lastName: 'Dot', revision: 1}
{id: 4, firstName: 'JonA', lastName: 'Doe', revision: 2}
{id: 4, firstName: 'John', lastName: 'Doe', revision: 3}
{id: 4, firstName: 'JohnB', lastName: 'Doe', revision: 4}

This way it is very easy to revert back to a revision since it contains the full object. The only requirement is that a fully loaded object is used when updating something.

Does that make sense?

Restore feature?

Does sequelize-paper-trail have a restore API yet? I see the intent in the tag line, but I don't see a documented feature in the README or code. Maybe I'm missing it. Maybe it hasn't been implemented yet. Thanks.

'document_id cannot be null' validation error occurs when using underscoredAttributes

This appears to be caused by the following code in lib/index.js:

      // Build revision
      var revision = Revision.build({
        model: this.name,
        documentId: instance.id,
        document: JSON.stringify(currentVersion),
        userId: ns.get(options.continuationKey) || opt[options.continuationKey]
      });

Note the hardcoded documentId property in the object passed to build, which should be document_id when underscoredAttributes is true.

JSON field does not work anymore in MySQL 5.7+

JSON field was changed to JSONB (I think its a Postgres specific field). In past versions of paper-trail, the JSON field it worked fine with MySQL 5.7+ versions.

I'm working in a patch to keep paper trail working both with Pg/MySQL native JSON fields.

User Tracking does not populate associated column

I have enabled user tracking by following the instructions on the readme. However, when we read Revision.build it does not populate the associated column.

So if I pass the userId to the sequelize options when adding or updating a model I get the following SQL:

Executing (default): INSERT INTO `revisions` (`model`,`document`,`operation`,`document_id`,`revision`,`id`,`created_at`,`updated_at`) VALUES (?,?,?,?,?,?,?,?);

Field 'model' doesn't have a default value in version 2.1.0

After update to version 2.1.0, cannot register any revision:

     sqlMessage: 'Field \'model\' doesn\'t have a default value',
     sql: 'INSERT INTO `revisions` (`id`,`createdAt`,`updatedAt`,`revision`) VALUES (DEFAULT,\'2019-01-03 03:58:29\',\'2019-01-03 03:58:29\',1);' },

Observe that some fields are not in SQL, like model, documentId, document and userId.

I located the problem in line 321 in opt param passed in save call.

If the opt is removed like in the old version, the function back work (I don't know why yet).

Origin commit e015126

User Table Required? modelName + ' has not been defined'

In lines 394-396 a relation is defined between the Revision table and the User table.

Is the user table a required table?

Later on in the code (lines 443-445): the same relation is set but there is a check to see if the userModel is provided.

Is this a bug? Lines 394-396 is causing the following error for the following code:

const PaperTrail = require('sequelize-paper-trail').init(app.get('sequelizeClient'), opts);
PaperTrail.defineModels({});
/Users/kcopeland/Projects/match-finder/node_modules/sequelize/lib/sequelize.js:356
      throw new Error(modelName + ' has not been defined');
      ^

Error: false has not been defined
    at Sequelize.model (/Users/kcopeland/Projects/match-finder/node_modules/sequelize/lib/sequelize.js:356:13)
    at Function.associate (/Users/kcopeland/Projects/match-finder/node_modules/sequelize-paper-trail/lib/index.js:395:40)
    at Object.keys.forEach.name (/Users/kcopeland/Projects/match-finder/src/sequelize.js:61:22)
    at Array.forEach (<anonymous>)
    at Function.app.setup (/Users/kcopeland/Projects/match-finder/src/sequelize.js:59:25)
    at Function.listen (/Users/kcopeland/Projects/match-finder/node_modules/@feathersjs/express/lib/index.js:63:12)
    at Function.listen (/Users/kcopeland/Projects/match-finder/node_modules/uberproto/lib/proto.js:30:17)
    at Object.<anonymous> (/Users/kcopeland/Projects/match-finder/src/index.js:5:20)
    at Module._compile (module.js:649:30)
    at Object.Module._extensions..js (module.js:660:10)
    at Module.load (module.js:561:32)
    at tryModuleLoad (module.js:501:12)
    at Function.Module._load (module.js:493:3)
    at Function.Module.runMain (module.js:690:10)
    at startup (bootstrap_node.js:194:16)
    at bootstrap_node.js:666:3

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.