quantal-base-model
Advanced tools
Comparing version 0.0.1 to 0.0.2
416
lib/index.js
@@ -1,271 +0,171 @@ | ||
const _ = require('lodash') | ||
const Errors = require('@quantal/errors') | ||
'use strict' | ||
module.exports = (bookshelf) => { | ||
const CommonErrors = require('quantal-errors') | ||
/** | ||
* Creates base model based on bookshelf-modelbase plugin see https://github.com/bsiddiqui/bookshelf-modelbase for more info | ||
* @param {Object} bookshelf - An initialised instance of bookshelf | ||
* @param {Object} [validations] - An object of Joi validations | ||
* @returns {*} | ||
* @constructor | ||
*/ | ||
const BaseModel = (bookshelf, validations) => { | ||
if (!bookshelf) { | ||
throw new Error('Must pass an initialized bookshelf instance') | ||
throw new CommonErrors.IllegalArgumentError('An initialised instance of bookshelf is required') | ||
} | ||
const ModelBase = require('bookshelf-modelbase')(bookshelf) | ||
bookshelf.plugin('bookshelf-modelbase') | ||
let BaseModel = bookshelf.Model.extend({ | ||
// It assumes the table has creation/update timestamp column. If it doesn't, the sub-Model needs to declare | ||
// `hasTimestamps: false` | ||
hasTimestamps: [ 'createdDate', 'lastUpdatedDate' ], | ||
delAttribute: 'is_deleted', | ||
const baseModel = ModelBase.extend({ | ||
// validation is passed to Joi.object(), so use a raw object | ||
validate: validations | ||
}, | ||
{ | ||
/** | ||
* Returns a single instance of a model | ||
* @param data - the data to pass fetch | ||
* @param options {Object} - the options to pass to model.fetch | ||
* @param {Boolean} [bConvertResultToJson=true] - if true, result will be converted to JSON, otherwise Bookself model instance will be returned | ||
* @returns {Promise} | ||
*/ | ||
findOne (data, options, bConvertResultToJson = true) { | ||
return bookshelf.Model.prototype.constructor.findOne.apply(this, arguments) | ||
.then(model => { | ||
if (!bConvertResultToJson) return model | ||
return model.toJSON() | ||
}) | ||
.catch(bookshelf.NotFoundError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(bookshelf.EmptyError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(err => Promise.reject(CommonErrors.utils.bookshelfToApp(err))) | ||
}, | ||
_transacting: null, | ||
_withRelated: [], | ||
_require: false, | ||
_debug: false, | ||
_includeRemoved: false, | ||
/** | ||
* Naive add - create and save a model based on data | ||
* @param {Object} data | ||
* @param {Object} options (optional) | ||
* @param {Boolean} [bConvertResultToJson=true] - if true, result will be converted to JSON, otherwise Bookself model instance will be returned | ||
* @return {Promise(bookshelf.Model)} single Model | ||
*/ | ||
create (data, options, bConvertResultToJson = true) { | ||
return bookshelf.Model.prototype.constructor.create.apply(this, arguments) | ||
.then(model => { | ||
if (!bConvertResultToJson) return model | ||
return model.toJSON() | ||
}) | ||
.catch(bookshelf.NotFoundError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(bookshelf.EmptyError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(err => Promise.reject(CommonErrors.utils.bookshelfToApp(err))) | ||
}, | ||
initialize: function () { | ||
this.on('created', () => { BaseModel.__resetOptions() }) | ||
this.on('destroyed', () => { BaseModel.__resetOptions() }) | ||
this.on('fetched', () => { BaseModel.__resetOptions() }) | ||
this.on('saved', () => { BaseModel.__resetOptions() }) | ||
this.on('updated', () => { BaseModel.__resetOptions() }) | ||
} | ||
}, { | ||
__optionsBuilder: function () { | ||
let options = {} | ||
if (this.prototype._withRelated) { | ||
Object.assign(options, { withRelated: this.prototype._withRelated }) | ||
} | ||
Object.assign(options, { | ||
require: this.prototype._require, | ||
debug: this.prototype._debug | ||
}) | ||
return options | ||
}, | ||
/** | ||
* Naive destroy | ||
* @param {Object} options | ||
* @param options.id {Number|String} - The id of the model to be deleted | ||
* @param {Boolean} [bConvertResultToJson=true] - if true, result will be converted to JSON, otherwise Bookself model instance will be returned | ||
* @return {Promise(bookshelf.Model)} empty Model | ||
*/ | ||
destroy (options, bConvertResultToJson = true) { | ||
return bookshelf.Model.prototype.constructor.destroy.apply(this, arguments) | ||
.then(model => { | ||
if (!bConvertResultToJson) return model | ||
return model.toJSON() | ||
}) | ||
.catch(bookshelf.NotFoundError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(bookshelf.EmptyError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(err => Promise.reject(CommonErrors.utils.bookshelfToApp(err))) | ||
}, | ||
__whereClauseBuilder: function () { | ||
let clause = {} | ||
if (this.prototype.delAttribute) { | ||
Object.assign(clause, { [this.prototype.delAttribute]: false }) | ||
} | ||
if (this.prototype.delAttribute && this.prototype._includeRemoved) { | ||
_.unset(clause, this.prototype.delAttribute) | ||
} | ||
return clause | ||
}, | ||
/** | ||
* Naive findAll - fetches all data for `this` | ||
* @param {Object} options (optional) | ||
* @param {Boolean} [bConvertResultToJson=true] - if true, result will be converted to JSON, otherwise Bookself model instance will be returned | ||
* @return {Promise(bookshelf.Collection)} Bookshelf Collection of all Models | ||
*/ | ||
findAll (options, bConvertResultToJson = true) { | ||
return bookshelf.Model.prototype.constructor.findAll.apply(this, arguments) | ||
.then(model => { | ||
if (!bConvertResultToJson) return model | ||
return model.toJSON() | ||
}) | ||
.catch(bookshelf.NotFoundError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(bookshelf.EmptyError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(err => Promise.reject(CommonErrors.utils.bookshelfToApp(err))) | ||
}, | ||
__resetOptions: function () { | ||
this.prototype._transacting = null | ||
this.prototype._withRelated = [] | ||
this.prototype._require = false | ||
this.prototype._debug = false | ||
this.prototype._includeRemoved = false | ||
}, | ||
/** | ||
* Find a model based on it's ID | ||
* @param {String | Number} id The model's ID | ||
* @param {Object} [options] Options used of model.fetch | ||
* @param {Boolean} [bConvertResultToJson=true] - if true, result will be converted to JSON, otherwise Bookself model instance will be returned | ||
* @return {Promise(bookshelf.Model)} | ||
*/ | ||
findById (id, options, bConvertResultToJson = true) { | ||
return bookshelf.Model.prototype.constructor.findById.apply(this, arguments) | ||
.then(model => { | ||
if (!bConvertResultToJson) return model | ||
return model.toJSON() | ||
}) | ||
.catch(bookshelf.NotFoundError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(bookshelf.EmptyError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(err => Promise.reject(CommonErrors.utils.bookshelfToApp(err))) | ||
}, | ||
transacting: function (t) { | ||
this.prototype._transacting = t | ||
return this | ||
}, | ||
/** | ||
* Find or create - try and find the model, create one if not found | ||
* @param {Object} data | ||
* @param {Object} options | ||
* @param {Boolean} [bConvertResultToJson=true] - if true, result will be converted to JSON, otherwise Bookself model instance will be returned | ||
* @return {Promise(bookshelf.Model)} single Model | ||
*/ | ||
findOrCreate (data, options, bConvertResultToJson = true) { | ||
return bookshelf.Model.prototype.constructor.findOrCreate.apply(this, arguments) | ||
.then(model => { | ||
if (!bConvertResultToJson) return model | ||
return model.toJSON() | ||
}) | ||
.catch(bookshelf.NotFoundError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(bookshelf.EmptyError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(err => Promise.reject(CommonErrors.utils.bookshelfToApp(err))) | ||
}, | ||
withRelated: function (...args) { | ||
this.prototype._withRelated = [...args] | ||
return this | ||
}, | ||
/** | ||
* Naive update - update a model based on data | ||
* @param {Object} data | ||
* @param {Object} options | ||
* @param {Boolean} [bConvertResultToJson=true] - if true, result will be converted to JSON, otherwise Bookself model instance will be returned | ||
* @return {Promise(bookshelf.Model)} edited Model | ||
*/ | ||
update (data, options, bConvertResultToJson = true) { | ||
return bookshelf.Model.prototype.constructor.update.apply(this, arguments) | ||
.then(model => { | ||
if (!bConvertResultToJson) return model | ||
return model.toJSON() | ||
}) | ||
.catch(bookshelf.NotFoundError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(bookshelf.EmptyError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(err => Promise.reject(CommonErrors.utils.bookshelfToApp(err))) | ||
}, | ||
withRelatedQuery: function (...args) { | ||
this.prototype._withRelated.push(...args) | ||
return this | ||
}, | ||
require: function (require = true) { | ||
this.prototype._require = require | ||
return this | ||
}, | ||
debug: function (debug = true) { | ||
this.prototype._debug = debug | ||
return this | ||
}, | ||
includeRemoved: function (include = true) { | ||
this.prototype._includeRemoved = include | ||
return this | ||
}, | ||
/** | ||
* Insert new entry | ||
* @param {Object} data - Entry object | ||
* @returns {Promise.<Object>} | ||
*/ | ||
insert: function (data) { | ||
let options = { | ||
method: 'insert', | ||
require: true | ||
/** | ||
* Upsert - select a model based on data and update if found, insert if not found | ||
* @param {Object} selectData Data for select | ||
* @param {Object} updateData Data for update | ||
* @param {Object} [options] Options for model.save | ||
* @param {Boolean} [bConvertResultToJson=true] - if true, result will be converted to JSON, otherwise Bookself model instance will be returned | ||
* @return {Promise(bookshelf.Model)} edited Model | ||
*/ | ||
upsert (selectData, options, bConvertResultToJson = true) { | ||
return bookshelf.Model.prototype.constructor.upsert.apply(this, arguments) | ||
.then(model => { | ||
if (!bConvertResultToJson) return model | ||
return model.toJSON() | ||
}) | ||
.catch(bookshelf.NotFoundError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(bookshelf.EmptyError, () => Promise.reject(new CommonErrors.NotFoundError('NotFoundError'))) | ||
.catch(err => Promise.reject(CommonErrors.utils.bookshelfToApp(err))) | ||
} | ||
}) | ||
return baseModel | ||
} | ||
if (this._transacting) { | ||
Object.assign(options, { transacting: this._transacting }) | ||
} | ||
return new this(data) | ||
.save(null, options) | ||
.then(newEntry => { | ||
this.__resetOptions() | ||
return newEntry.toJSON() | ||
}) | ||
.catch(err => Promise.reject(Errors.utils.bookshelfToC8(err))) | ||
}, | ||
/** | ||
* | ||
* @param opts | ||
* @returns {Promise.<Object>} | ||
*/ | ||
findAll: function (opts) { | ||
let clause = this.__whereClauseBuilder() | ||
let options = this.__optionsBuilder() | ||
return this | ||
.where(clause) | ||
.fetchAll(options) | ||
.then((entries) => { | ||
this.__resetOptions() | ||
return entries.invokeThen('toJSON') | ||
}) | ||
.catch(this.EmptyError, () => Promise.reject(new Errors.NotFoundError('NotFoundError'))) | ||
.catch(err => Promise.reject(Errors.utils.bookshelfToC8(err))) | ||
}, | ||
/** | ||
* | ||
* @param id | ||
* @param opts | ||
* @returns {Promise.<TResult>} | ||
*/ | ||
findById: function (id, opts) { | ||
let options = this.__optionsBuilder() | ||
let delAttribute | ||
const where = { | ||
[this.prototype.idAttribute]: id | ||
} | ||
if (this.prototype.delAttribute) { | ||
// Soft-remove enabled, set where clause to only fetch non-removed items. | ||
delAttribute = _.snakeCase(this.prototype.delAttribute) | ||
where[delAttribute] = false | ||
} | ||
if (opts && (this.prototype.delAttribute && opts.includeRemoved)) { | ||
// Show soft-removed items as well. | ||
delete where[delAttribute] | ||
} | ||
return this | ||
.where(where) | ||
.fetch(options) | ||
.then((campaign) => { | ||
this.__resetOptions() | ||
return campaign ? campaign.toJSON() : {} | ||
}) | ||
.catch(this.NotFoundError, err => Promise.reject(new Errors.NotFoundError(err))) | ||
.catch(err => Promise.reject(Errors.utils.bookshelfToC8(err))) | ||
}, | ||
/** | ||
* | ||
* @param where | ||
* @param opts | ||
* @returns {Promise.<TResult>} | ||
*/ | ||
findWhere: function (where, opts) { | ||
if (this.prototype.delAttribute) { | ||
// Soft-remove enabled, set where clause to only fetch non-removed items. | ||
where[this.prototype.delAttribute] = false | ||
} | ||
if (opts && (this.prototype.delAttribute && opts.includeRemoved)) { | ||
// Show soft-removed items as well. | ||
delete where[this.prototype.delAttribute] | ||
} | ||
where = _.mapKeys(where, (v, k) => { | ||
return _.snakeCase(k) | ||
}) | ||
return this | ||
.where(where) | ||
.fetchAll({ require: true }) | ||
.then((entries) => { | ||
this.__resetOptions() | ||
return entries.invokeThen('toJSON') | ||
}) | ||
.catch(this.EmptyError, () => Promise.reject(new Errors.NotFoundError('NotFoundError'))) | ||
.catch(err => Promise.reject(Errors.utils.bookshelfToC8(err))) | ||
}, | ||
/** | ||
* | ||
* @param id | ||
* @returns {Promise.<T>} | ||
*/ | ||
removeById: function (id) { | ||
let options = this.__optionsBuilder() | ||
return Promise | ||
.all([]) | ||
.then(() => this.prototype.delAttribute | ||
? new this({ [this.prototype.idAttribute]: id }) | ||
.where({ [this.prototype.delAttribute]: false }) | ||
.save({ [this.prototype.delAttribute]: true }, _.assign(options, { patch: true })) | ||
.catch(this.NoRowsUpdatedError, () => Promise.reject(new this.NoRowsDeletedError(`Entry with id:${id} not deleted`))) | ||
: new this({ [this.prototype.idAttribute]: id }) | ||
.destroy(options) | ||
.catch(this.NoRowsDeletedError, () => Promise.reject(new this.NoRowsDeletedError(`Entry with id:${id} not deleted`))) | ||
) | ||
.then(removedEntry => removedEntry.toJSON()) | ||
.catch(err => Promise.reject(Errors.utils.bookshelfToC8(err))) | ||
}, | ||
/** | ||
* | ||
* @param id | ||
* @param data | ||
* @param opts | ||
* @returns {Promise.<TResult>} | ||
*/ | ||
updateById: function (id, data, opts) { | ||
let delAttribute | ||
const where = { | ||
[this.prototype.idAttribute]: id | ||
} | ||
if (this.prototype.delAttribute) { | ||
// Soft-remove enabled, set where clause to only fetch non-removed items. | ||
delAttribute = _.snakeCase(this.prototype.delAttribute) | ||
where[delAttribute] = false | ||
} | ||
if (opts && (this.prototype.delAttribute && opts.includeRemoved)) { | ||
// Show soft-removed items as well. | ||
delete where[delAttribute] | ||
} | ||
return this | ||
.where(where) | ||
.fetch({ require: true }) | ||
.then((entry) => { | ||
return entry | ||
.save(data, { | ||
method: 'update', | ||
require: true, | ||
patch: true | ||
}) | ||
.then((entry) => { | ||
this.__resetOptions() | ||
return entry.toJSON() | ||
}) | ||
}) | ||
.catch(this.NotFoundError, () => Promise.reject(new Errors.NotFoundError('NotFoundError'))) | ||
.catch(err => Promise.reject(Errors.utils.bookshelfToC8(err))) | ||
}, | ||
EmptyError: bookshelf.Collection.EmptyError | ||
}) | ||
return BaseModel | ||
} | ||
module.exports = BaseModel |
{ | ||
"name": "quantal-base-model", | ||
"version": "0.0.1", | ||
"description": "A base model for all bookshelf-based models on your database layer.", | ||
"engines": { | ||
"node": ">=6" | ||
}, | ||
"version": "0.0.2", | ||
"main": "lib/index.js", | ||
"scripts": { | ||
"coverage": "istanbul cover _mocha --print both && istanbul check-coverage", | ||
"standard": "standard --verbose", | ||
"test": "NODE_ENV=test mocha" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/quophyie/base-model.git" | ||
}, | ||
"keywords": [ | ||
"bookshelf", | ||
"model" | ||
], | ||
"author": "quophyie ", | ||
"bugs": { | ||
"url": "https://github.com/quophyie/base-model/issues" | ||
}, | ||
"homepage": "https://github.com/quophyie/base-model#readme", | ||
"dependencies": { | ||
"quantal-errors": "^0.0.x", | ||
"lodash": "^4.17.4" | ||
"bookshelf-modelbase": "^2.10.3", | ||
"quantal-errors": "^0.0.4" | ||
}, | ||
"license": "ISC", | ||
"devDependencies": { | ||
"bookshelf": "^0.10.3", | ||
"bookshelf-camelcase": "^1.1.4", | ||
"code": "^4.0.0", | ||
@@ -42,8 +17,5 @@ "eslint": "^3.17.1", | ||
"eslint-plugin-standard": "^2.1.1", | ||
"istanbul": "^0.4.5", | ||
"knex": "^0.12.6", | ||
"mocha": "^3.2.0", | ||
"pg": "^6.1.2", | ||
"standard": "^8.6.0" | ||
"mocha": "^2.4.5", | ||
"standard": "^9.0.1" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 4 instances in 1 package
51613
9
1
12
1
160
3
2
2
0
1
+ Addedbookshelf-modelbase@^2.10.3
+ Addedbookshelf-modelbase@2.11.0(transitive)
+ Addedhoek@6.1.3(transitive)
+ Addedisemail@3.2.0(transitive)
+ Addedjoi@14.3.1(transitive)
+ Addedlodash.difference@4.5.0(transitive)
+ Addedquantal-errors@0.0.4(transitive)
+ Addedtopo@3.0.3(transitive)
- Removedlodash@^4.17.4
- Removedquantal-errors@0.0.14(transitive)
Updatedquantal-errors@^0.0.4