From 375fa0fdaad4da6c2931010db58a466721f3d111 Mon Sep 17 00:00:00 2001 From: Toma Zelic Date: Fri, 19 Mar 2021 16:46:34 +0100 Subject: [PATCH 1/4] Align validation errors API and extend Error class --- lib/errors/validation/attribute-error.js | 14 +++++------- lib/errors/validation/consumable-error.js | 15 ++++++------- lib/errors/validation/domain-error.js | 15 +++++-------- lib/errors/validation/fk-error.js | 24 +++++++++----------- lib/errors/validation/uniqueness-error.js | 22 +++++++++--------- lib/errors/validation/validation-error.js | 27 +++++++++-------------- 6 files changed, 50 insertions(+), 67 deletions(-) diff --git a/lib/errors/validation/attribute-error.js b/lib/errors/validation/attribute-error.js index c8ea7aa..a25421d 100644 --- a/lib/errors/validation/attribute-error.js +++ b/lib/errors/validation/attribute-error.js @@ -2,16 +2,12 @@ const ValidationError = require('./validation-error'); -class AttributeError extends ValidationError { +class AttributeValidationError extends ValidationError { constructor(originalError) { - super(originalError.instance); - this.name = 'AttributeError'; - this.original = originalError; - } - - get message() { - return `${this.name}: ${this.original.message}; ${this.instanceInfo}`; + const { instance, message } = originalError; + super(instance, message); + this.originalError = originalError; } } -module.exports = AttributeError; +module.exports = AttributeValidationError; diff --git a/lib/errors/validation/consumable-error.js b/lib/errors/validation/consumable-error.js index cf84593..ddb3f4e 100644 --- a/lib/errors/validation/consumable-error.js +++ b/lib/errors/validation/consumable-error.js @@ -2,16 +2,15 @@ const ValidationError = require('./validation-error'); -class ConsumableError extends ValidationError { +class ConsumableValidationError extends ValidationError { constructor(instance, association) { - super(...arguments); - this.name = 'ConsumableError'; + super(instance, getMessage(association)); this.association = association; } - - get message() { - return `${this.name}: Invalid consumable association: ${this.association.associationAccessor}; ${this.instanceInfo}`; - } } -module.exports = ConsumableError; +module.exports = ConsumableValidationError; + +function getMessage(association) { + return `Invalid consumable association: ${association.associationAccessor}`; +} diff --git a/lib/errors/validation/domain-error.js b/lib/errors/validation/domain-error.js index 63a3d89..a211e8d 100644 --- a/lib/errors/validation/domain-error.js +++ b/lib/errors/validation/domain-error.js @@ -2,16 +2,11 @@ const ValidationError = require('./validation-error'); -class DomainError extends ValidationError { - constructor(instance, originalMessage) { - super(...arguments); - this.name = 'DomainError'; - this.originalMessage = originalMessage; - } - - get message() { - return `${this.name}: ${this.originalMessage}; ${this.instanceInfo}`; +class DomainValidationError extends ValidationError { + constructor(instance, message) { + super(instance, message); + this.originalMessage = message; } } -module.exports = DomainError; +module.exports = DomainValidationError; diff --git a/lib/errors/validation/fk-error.js b/lib/errors/validation/fk-error.js index 3db83e8..d877c0e 100644 --- a/lib/errors/validation/fk-error.js +++ b/lib/errors/validation/fk-error.js @@ -2,21 +2,19 @@ const ValidationError = require('./validation-error'); -class ValidationFkError extends ValidationError { +class ForeignKeyValidationError extends ValidationError { constructor(instance, association) { - super(...arguments); - this.name = 'ValidationFkError'; - this.alias = association.associationAccessor; - this.fk = association.identifier; + const alias = association.associationAccessor; + const fk = association.identifier; + const value = instance[fk]; + const message = getMessage(alias, fk, value); + super(instance, message); + Object.assign(this, { alias, fk, value }); } +} - get message() { - return `${this.name}: No ${this.alias} found for ${this.fk}: ${this.value}; ${this.instanceInfo}`; - } +module.exports = ForeignKeyValidationError; - get value() { - return this.instance[this.fk]; - } +function getMessage(alias, fk, value) { + return `No ${alias} found for ${fk}: ${value}`; } - -module.exports = ValidationFkError; diff --git a/lib/errors/validation/uniqueness-error.js b/lib/errors/validation/uniqueness-error.js index 769faf0..5e1ddc0 100644 --- a/lib/errors/validation/uniqueness-error.js +++ b/lib/errors/validation/uniqueness-error.js @@ -5,20 +5,16 @@ const map = require('lodash/map'); const transform = require('lodash/transform'); const ValidationError = require('./validation-error'); -class ValidationUniquenessError extends ValidationError { - constructor(instance, index) { - super(...arguments); - this.name = 'ValidationUniquenessError'; - this.index = index; - this.values = parseValues(instance, index); - } - - get message() { - return `${this.name}: duplicate value for ${this.index.join()}: ${map(this.values).join()}; ${this.instanceInfo}`; +class UniquenessValidationError extends ValidationError { + constructor(instance, index = []) { + const values = parseValues(instance, index); + const message = getMessage(index, values); + super(instance, message); + Object.assign(this, { values, index }); } } -module.exports = ValidationUniquenessError; +module.exports = UniquenessValidationError; function parseValues(instance, index) { return transform(index, (acc, it) => { @@ -26,3 +22,7 @@ function parseValues(instance, index) { acc[it] = instance[attribute]; }, {}); } + +function getMessage(index, values) { + return `Duplicate value for ${index.join()}: ${map(values).join()}`; +} diff --git a/lib/errors/validation/validation-error.js b/lib/errors/validation/validation-error.js index 86bb4e6..ed35714 100644 --- a/lib/errors/validation/validation-error.js +++ b/lib/errors/validation/validation-error.js @@ -1,22 +1,17 @@ 'use strict'; -class ValidationError { - constructor(instance) { - this.name = 'ValidationError'; - this.instance = instance; - } - - get id() { - return this.instance.id; - } - - get modelName() { - return this.instance.constructor.name; - } - - get instanceInfo() { - return `${this.modelName} id: ${this.id}`; +class ValidationError extends Error { + constructor(instance, message = '') { + const modelName = instance.constructor.name; + const id = instance.id; + super(getMessage(message, modelName, id)); + Object.assign(this, { instance, id, modelName }); } } module.exports = ValidationError; + +function getMessage(message, modelName, id) { + const instanceInfo = `${modelName} id: ${id}`; + return [message, instanceInfo].filter(Boolean).join('; '); +} From 2dba52ddf3dec7bd4c4950c5fb5b96d6b253a8e3 Mon Sep 17 00:00:00 2001 From: Toma Zelic Date: Fri, 19 Mar 2021 17:59:44 +0100 Subject: [PATCH 2/4] Hide error generation from stack trace --- lib/errors/validation/validation-error.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/errors/validation/validation-error.js b/lib/errors/validation/validation-error.js index ed35714..0fd1ef4 100644 --- a/lib/errors/validation/validation-error.js +++ b/lib/errors/validation/validation-error.js @@ -6,6 +6,7 @@ class ValidationError extends Error { const id = instance.id; super(getMessage(message, modelName, id)); Object.assign(this, { instance, id, modelName }); + Error.captureStackTrace(this, this.constructor); } } From 5980b8c63cfd64919d8ea192606c3898da745638 Mon Sep 17 00:00:00 2001 From: Toma Zelic Date: Fri, 19 Mar 2021 18:20:36 +0100 Subject: [PATCH 3/4] Introduce base SyncError --- lib/errors/sync-error.js | 12 ++++++++++++ lib/errors/validation/validation-error.js | 11 ++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 lib/errors/sync-error.js diff --git a/lib/errors/sync-error.js b/lib/errors/sync-error.js new file mode 100644 index 0000000..78a667b --- /dev/null +++ b/lib/errors/sync-error.js @@ -0,0 +1,12 @@ +'use strict'; + +class SyncError extends Error { + constructor(message) { + super(message); + this.createdAt = Date.now(); + this.name = this.constructor.name; + Error.captureStackTrace(this, this.constructor); + } +} + +module.exports = SyncError; diff --git a/lib/errors/validation/validation-error.js b/lib/errors/validation/validation-error.js index 0fd1ef4..19f9c71 100644 --- a/lib/errors/validation/validation-error.js +++ b/lib/errors/validation/validation-error.js @@ -1,12 +1,13 @@ 'use strict'; -class ValidationError extends Error { +const SyncError = require('../sync-error'); + +class ValidationError extends SyncError { constructor(instance, message = '') { const modelName = instance.constructor.name; - const id = instance.id; - super(getMessage(message, modelName, id)); - Object.assign(this, { instance, id, modelName }); - Error.captureStackTrace(this, this.constructor); + const instanceId = instance.id; + super(getMessage(message, modelName, instanceId)); + Object.assign(this, { instance, instanceId, modelName }); } } From e123bb32c3e7e72469f841e611cfa719134b15d3 Mon Sep 17 00:00:00 2001 From: Toma Zelic Date: Fri, 19 Mar 2021 19:09:50 +0100 Subject: [PATCH 4/4] Scope operational errors --- lib/arbiquelize/index.js | 3 +-- lib/errors/index.js | 11 ++++++----- lib/errors/operational/index.js | 5 +++++ lib/errors/operational/operational-error.js | 5 +++++ .../{ => operational}/sf-persistance-error.js | 6 +++--- lib/errors/operational/sync-module-pull-error.js | 13 +++++++++++++ lib/errors/sync-module-pull-error.js | 12 ------------ lib/sync/sync-module-validator.js | 2 +- lib/sync/sync-module.js | 4 +--- 9 files changed, 35 insertions(+), 26 deletions(-) create mode 100644 lib/errors/operational/index.js create mode 100644 lib/errors/operational/operational-error.js rename lib/errors/{ => operational}/sf-persistance-error.js (78%) create mode 100644 lib/errors/operational/sync-module-pull-error.js delete mode 100644 lib/errors/sync-module-pull-error.js diff --git a/lib/arbiquelize/index.js b/lib/arbiquelize/index.js index c6ecfae..f1dd5b9 100644 --- a/lib/arbiquelize/index.js +++ b/lib/arbiquelize/index.js @@ -2,7 +2,6 @@ const Arbiter = require('./arbiter'); const difference = require('lodash/difference'); -const errors = require('../errors'); const filter = require('lodash/filter'); const flatMap = require('lodash/flatMap'); const { format } = require('util'); @@ -16,6 +15,7 @@ const pick = require('lodash/pick'); const Promise = require('bluebird'); const reduce = require('lodash/reduce'); const sortBy = require('lodash/sortBy'); +const { SfPersistanceError } = require('../errors'); const Sqlize = require('../sqlize'); const { Sequelize } = Sqlize; @@ -25,7 +25,6 @@ const isModel = Ctor => Ctor && Ctor.prototype instanceof Arbiquelize.Model; const isProduction = process.env.NODE_ENV === 'production'; const { DataTypes } = Sequelize; -const { SfPersistanceError } = errors; const { validate } = DataTypes.STRING.prototype; DataTypes.STRING.prototype.validate = function (value, options) { let isValid = validate.call(this, value, options); diff --git a/lib/errors/index.js b/lib/errors/index.js index 5ff2af2..6e54d72 100644 --- a/lib/errors/index.js +++ b/lib/errors/index.js @@ -1,7 +1,8 @@ 'use strict'; -exports.ErrorManager = require('./error-manager'); - -exports.SfPersistanceError = require('./sf-persistance-error'); -exports.SyncModulePullError = require('./sync-module-pull-error'); -exports.Validation = require('./validation'); +module.exports = { + ErrorManager: require('./error-manager'), + SyncError: require('./error-manager'), + ...require('./validation'), + ...require('./operational') +}; diff --git a/lib/errors/operational/index.js b/lib/errors/operational/index.js new file mode 100644 index 0000000..f5d5db0 --- /dev/null +++ b/lib/errors/operational/index.js @@ -0,0 +1,5 @@ +'use strict'; + +exports.OperationalError = require('./operational-error'); +exports.SfPersistanceError = require('./sf-persistance-error'); +exports.SyncModulePullError = require('./sync-module-pull-error'); diff --git a/lib/errors/operational/operational-error.js b/lib/errors/operational/operational-error.js new file mode 100644 index 0000000..31b0f42 --- /dev/null +++ b/lib/errors/operational/operational-error.js @@ -0,0 +1,5 @@ +'use strict'; + +const SyncError = require('../sync-error'); + +module.exports = class OperationalError extends SyncError {}; diff --git a/lib/errors/sf-persistance-error.js b/lib/errors/operational/sf-persistance-error.js similarity index 78% rename from lib/errors/sf-persistance-error.js rename to lib/errors/operational/sf-persistance-error.js index 0997e55..3bfc21d 100644 --- a/lib/errors/sf-persistance-error.js +++ b/lib/errors/operational/sf-persistance-error.js @@ -2,14 +2,14 @@ const find = require('lodash/find'); const matchAll = require('match-all'); +const OperationalError = require('./operational-error'); -class SfPersistanceError { +class SfPersistanceError extends OperationalError { constructor(original, Model, entries) { - this.name = 'SfPersistanceError'; + super(`Unable to persist fetched ${Model.name} salesforce entries`); this.original = original; this.Model = Model; this.entries = entries; - this.message = `Unable to persist fetched ${Model.name} salesforce entries`; if (!this[original.name]) throw original; this.failingField = this[original.name](); diff --git a/lib/errors/operational/sync-module-pull-error.js b/lib/errors/operational/sync-module-pull-error.js new file mode 100644 index 0000000..a4dcafd --- /dev/null +++ b/lib/errors/operational/sync-module-pull-error.js @@ -0,0 +1,13 @@ +'use strict'; + +const OperationalError = require('./operational-error'); + +class SyncModulePullError extends OperationalError { + constructor(original, SyncModule) { + super(`Error pulling ${SyncModule.Model.name} salesforce records`); + this.original = original; + this.SyncModule = SyncModule; + } +} + +module.exports = SyncModulePullError; diff --git a/lib/errors/sync-module-pull-error.js b/lib/errors/sync-module-pull-error.js deleted file mode 100644 index 4cfef24..0000000 --- a/lib/errors/sync-module-pull-error.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -class SyncModulePullError { - constructor(original, SyncModule) { - this.name = 'SyncModulePullError'; - this.original = original; - this.SyncModule = SyncModule; - this.message = `Error pulling ${SyncModule.Model.name} salesforce records`; - } -} - -module.exports = SyncModulePullError; diff --git a/lib/sync/sync-module-validator.js b/lib/sync/sync-module-validator.js index e4cf812..340c46d 100644 --- a/lib/sync/sync-module-validator.js +++ b/lib/sync/sync-module-validator.js @@ -1,6 +1,6 @@ 'use strict'; -const errors = require('../errors/validation'); +const errors = require('../errors'); const filter = require('lodash/filter'); const map = require('lodash/map'); const Promise = require('bluebird'); diff --git a/lib/sync/sync-module.js b/lib/sync/sync-module.js index 7dfa63f..9cb7814 100644 --- a/lib/sync/sync-module.js +++ b/lib/sync/sync-module.js @@ -1,6 +1,6 @@ 'use strict'; -const errors = require('../errors'); +const { ErrorManager, SfPersistanceError, SyncModulePullError } = require('../errors'); const filter = require('lodash/filter'); const Loader = require('../utils/loader'); const once = require('lodash/once'); @@ -8,8 +8,6 @@ const partition = require('lodash/partition'); const Promise = require('bluebird'); const SyncModuleValidator = require('./sync-module-validator'); -const { ErrorManager, SfPersistanceError, SyncModulePullError } = errors; - class SyncModule { constructor({ Model, sync }) { this.Model = Model;