From a668faefa347e8a11220384cb8fcaf7dd827920d Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Thu, 6 Aug 2020 00:31:41 +0200 Subject: [PATCH] Add error code support --- example.js | 6 +-- index.js | 116 ++++++++++++++++++++++++++++------------------------- yarn.lock | 94 +++++++++++++++++++++++++++++++++---------- 3 files changed, 138 insertions(+), 78 deletions(-) diff --git a/example.js b/example.js index 43be346..4eb1d40 100644 --- a/example.js +++ b/example.js @@ -6,7 +6,7 @@ const wrapError = require("./"); /*** Simple rules ***/ -let rule = wrapError("This is a custom error message complaining that the value must be a string!", [ isString ]); +let rule = wrapError("wrap-error.custom", "This is a custom error message complaining that the value must be a string!", [ isString ]); console.log(validateValue("hello world", [ rule ])); // hello world @@ -21,7 +21,7 @@ try { /*** Combinators with properties, and preserving the original errors as well ***/ -let objectRule = wrapError("Must be a valid thingem", { +let objectRule = wrapError("wrap-error.thingem", "Must be a valid thingem", { a: [ isString ], b: [ isString ] }, { preserveOriginalErrors: true }); @@ -35,5 +35,5 @@ let objectB = { a: "hello", b: 42 }; console.log(validateValue(objectB, [ objectRule ])); /* AggregrateValidationError: One or more validation errors occurred: - At (root): Must be a valid thingem - - At b: Must be a string + └─ b: Must be a string */ diff --git a/index.js b/index.js index f24cd12..aa3b66c 100644 --- a/index.js +++ b/index.js @@ -17,68 +17,76 @@ function concat(arrays) { }, arrays[0]); } -module.exports = function wrapError(message, rules, options = {}) { - let preserveOriginalErrors = defaultValue(options.preserveOriginalErrors, false); +module.exports = function wrapError(code, message, rules, options = {}) { + if (typeof code !== "string") { + throw new Error(`'code' argument must be a string`); + } else if (typeof message !== "string") { + throw new Error(`'message' argument must be a string`); + } else if (rules == null) { + throw new Error(`'rules' argument is required`); + } else { + let preserveOriginalErrors = defaultValue(options.preserveOriginalErrors, false); - return combinator((value, applyValidators, context) => { - let result = applyValidators(value, rules, context); + return combinator((value, applyValidators, context) => { + let result = applyValidators(value, rules, context); - if (result.errors.length > 0) { - let { errors, newValue } = result; + if (result.errors.length > 0) { + let { errors, newValue } = result; - let errorsByType = splitFilterN(errors, [ "validationRoot", "validationPath", "other" ], (error) => { - if (matchValidationError(error)) { - if (error.path.length === 0) { - return "validationRoot"; + let errorsByType = splitFilterN(errors, [ "validationRoot", "validationPath", "other" ], (error) => { + if (matchValidationError(error)) { + if (error.path.length === 0) { + return "validationRoot"; + } else { + return "validationPath"; + } } else { - return "validationPath"; + return "other"; } - } else { - return "other"; - } - }); + }); - let hasRootValidationErrors = errorsByType.validationRoot.length > 0; - let hasPathValidationErrors = errorsByType.validationPath.length > 0; - let hasValidationErrors = hasRootValidationErrors || hasPathValidationErrors; + let hasRootValidationErrors = errorsByType.validationRoot.length > 0; + let hasPathValidationErrors = errorsByType.validationPath.length > 0; + let hasValidationErrors = hasRootValidationErrors || hasPathValidationErrors; - let transformedValidationErrors = asExpression(() => { - if (hasValidationErrors) { - if (!preserveOriginalErrors) { - return [ new ValidationError(message) ]; - } else { - // If possible, we try to move any subpath errors into the subErrors of an (optionally newly-created) root error. Otherwise, it can become difficult for the user to correlate together the root and subpath errors that are related, when we start changing the messages of the root errors. - if (errorsByType.validationRoot.length === 0) { - return [ new ValidationError(message, { subErrors: errorsByType.validationPath }) ]; - } else if (errorsByType.validationRoot.length === 1) { - let error = errorsByType.validationRoot[0]; - - // TODO: Currently we cannot set `originalError` due to a bug in `create-error`; switch to a better error implementation - return [ new ValidationError(`${message} (${error.message})`, { subErrors: errorsByType.validationPath }) ]; + let transformedValidationErrors = asExpression(() => { + if (hasValidationErrors) { + if (!preserveOriginalErrors) { + return [ new ValidationError(message, { code: code }) ]; } else { - // If there are multiple root errors, we cannot determine which root error the subpath errors belong to, so we'll just provide them as a flat list - return concat([ - errorsByType.validationRoot.map((error) => { - return new ValidationError(`${message} (${error.message})`) - }), - errorsByType.validationPath - ]); + // If possible, we try to move any subpath errors into the subErrors of an (optionally newly-created) root error. Otherwise, it can become difficult for the user to correlate together the root and subpath errors that are related, when we start changing the messages of the root errors. + if (errorsByType.validationRoot.length === 0) { + return [ new ValidationError(message, { code: code, subErrors: errorsByType.validationPath }) ]; + } else if (errorsByType.validationRoot.length === 1) { + let error = errorsByType.validationRoot[0]; + + // TODO: Currently we cannot set `originalError` due to a bug in `create-error`; switch to a better error implementation + return [ new ValidationError(`${message} (${error.message})`, { code: code, subErrors: errorsByType.validationPath }) ]; + } else { + // If there are multiple root errors, we cannot determine which root error the subpath errors belong to, so we'll just provide them as a flat list + return concat([ + errorsByType.validationRoot.map((error) => { + return new ValidationError(`${message} (${error.message})`, { code: code }) + }), + errorsByType.validationPath + ]); + } } + } else { + return []; } - } else { - return []; - } - }); - - return validationResult({ - newValue: newValue, - errors: concat([ - transformedValidationErrors, - errorsByType.other - ]) - }); - } else { - return result; - } - }); + }); + + return validationResult({ + newValue: newValue, + errors: concat([ + transformedValidationErrors, + errorsByType.other + ]) + }); + } else { + return result; + } + }); + } }; diff --git a/yarn.lock b/yarn.lock index 6a1ae46..b1ff157 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,9 +10,9 @@ "@validatem/match-validation-error" "^0.1.0" "@validatem/any-property@^0.1.0": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@validatem/any-property/-/any-property-0.1.2.tgz#e6cced130ced057ab185d1a61034b77cb277a5ad" - integrity sha512-WHiUM9oKTVc3w1ZWXb2c09yx6pT65Z9oPJC6/rEJtygtsL+H5B8PlAAH16BllPxLF1ebeLlEhXs3qwau5I0USQ== + version "0.1.3" + resolved "https://registry.yarnpkg.com/@validatem/any-property/-/any-property-0.1.3.tgz#fc7768c1922a8bacff9369ae48913672e5350f52" + integrity sha512-jYWxif5ff9pccu7566LIQ/4+snlApXEJUimBywzAriBgS3r4eDBbz3oZFHuiPmhxNK/NNof5YUS+L6Sk3zaMfg== dependencies: "@validatem/annotate-errors" "^0.1.2" "@validatem/combinator" "^0.1.0" @@ -22,47 +22,51 @@ default-value "^1.0.0" "@validatem/combinator@^0.1.0", "@validatem/combinator@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@validatem/combinator/-/combinator-0.1.1.tgz#202f31243f8d57cf87f1b449405b134e2fa40c5a" - integrity sha512-crzAYCmKUcb1DC5sSpdof4gWHX81VRmm+REWflhFuRlKH6JHRV5RcBCxEjlDfRrxW2yF6s9i0rQAOyVVE+GGAg== + version "0.1.2" + resolved "https://registry.yarnpkg.com/@validatem/combinator/-/combinator-0.1.2.tgz#eab893d55f1643b9c6857eaf6ff7ed2a728e89ff" + integrity sha512-vE8t1tNXknmN62FlN6LxQmA2c6TwVKZ+fl/Wit3H2unFdOhu7SZj2kRPGjAXdK/ARh/3svYfUBeD75pea0j1Sw== "@validatem/core@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@validatem/core/-/core-0.3.1.tgz#33ea24ba4d4c516b5836703aea7e7da0afb6c227" - integrity sha512-k9OgvVqxX6rKmnkEQGi14pBqsilXu8pIVelaQsI/3tI5xVDDE5podpuPAJVrIsepy2berCtN9KSD/NCDhX1wLg== + version "0.3.12" + resolved "https://registry.yarnpkg.com/@validatem/core/-/core-0.3.12.tgz#e4e8a566850571bf55412862e88a3b06e75c8072" + integrity sha512-ngrFk6PT/pPZntpleG6q55SByongNxRk7wJhUiCihyv4yqIqqG+bNGH4wb6yW33IHefreWxkkJ53yM1Yj9srNA== dependencies: "@validatem/annotate-errors" "^0.1.2" "@validatem/any-property" "^0.1.0" "@validatem/error" "^1.0.0" - "@validatem/is-plain-object" "^0.1.0" "@validatem/match-validation-error" "^0.1.0" "@validatem/match-versioned-special" "^0.1.0" "@validatem/match-virtual-property" "^0.1.0" "@validatem/normalize-rules" "^0.1.0" "@validatem/required" "^0.1.0" "@validatem/validation-result" "^0.1.1" + "@validatem/virtual-property" "^0.1.0" + as-expression "^1.0.0" assure-array "^1.0.0" create-error "^0.3.1" default-value "^1.0.0" + execall "^2.0.0" flatten "^1.0.3" + indent-string "^4.0.0" is-arguments "^1.0.4" + supports-color "^7.1.0" + syncpipe "^1.0.0" "@validatem/error@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@validatem/error/-/error-1.0.0.tgz#a975904aa4c3e7618d89088a393567a5e1778340" - integrity sha512-7M3tV4DhCuimuCRdC2L/topBByDjhzspzeQGNU0S4/mdn2aDNtESYE43K/2Kh/utCAhqXh2gyw89WYxy//t3fQ== - dependencies: - create-error "^0.3.1" + version "1.1.0" + resolved "https://registry.yarnpkg.com/@validatem/error/-/error-1.1.0.tgz#bef46e7066c39761b494ebe3eec2ecdc7348f4ed" + integrity sha512-gZJEoZq1COi/8/5v0fVKQ9uX54x5lb5HbV7mzIOhY6dqjmLNfxdQmpECZPQrCAOpcRkRMJ7zaFhq4UTslpY9yA== "@validatem/has-shape@^0.1.0": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@validatem/has-shape/-/has-shape-0.1.4.tgz#678ebcd2864628515531d75e1b5491387a97bfd9" - integrity sha512-MVN4BOxRfsa2D95Lwp8Dh9I8paAhTYgBpZbDAFnyEv51qlGTQPeAnunnslFS+K38QLa9E96Dk5Vs8Pn7F5XH9g== + version "0.1.8" + resolved "https://registry.yarnpkg.com/@validatem/has-shape/-/has-shape-0.1.8.tgz#dff0f0449c12b96d150091b7a980154d810ae63d" + integrity sha512-x2i8toW1uraFF2Vl6WBl4CScbBeg5alrtoCKMyXbJkHf2B5QxL/ftUh2RQRcBzx6U0i7KUb8vdShcWAa+fehRQ== dependencies: "@validatem/annotate-errors" "^0.1.2" "@validatem/combinator" "^0.1.0" "@validatem/error" "^1.0.0" "@validatem/validation-result" "^0.1.1" + array-union "^2.1.0" as-expression "^1.0.0" assure-array "^1.0.0" default-value "^1.0.0" @@ -107,9 +111,9 @@ integrity sha512-ssd3coFgwbLuqvZftLZTy3eHN0TFST8oTS2XTViQdXJPXVoJmwEKBpFhXgwnb5Ly1CE037R/KWpjhd1TP/56kQ== "@validatem/normalize-rules@^0.1.0": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@validatem/normalize-rules/-/normalize-rules-0.1.2.tgz#6529f17a6f36c6e2ae3ef285c59347c2ea208aa1" - integrity sha512-IHc81Sy/W0OiCbmvE3kTB+5OPVJnXWHP2tTXvKO6hVH0qykclMvIPRGgZf1s4dLaeOLKdkkfKyO/pLTVyNCIbA== + version "0.1.3" + resolved "https://registry.yarnpkg.com/@validatem/normalize-rules/-/normalize-rules-0.1.3.tgz#59fd6193b1091ff97b5c723b32c9bb1fe2a9dc9c" + integrity sha512-HHPceAP2ce9NWymIZrgLCTzpdwXNRBCCB5H6ZPc5ggOrbmh4STpT83fLazleHtvYNlqgXZ4GjQOvCwrjaM+qEA== dependencies: "@validatem/has-shape" "^0.1.0" "@validatem/is-plain-object" "^0.1.0" @@ -136,6 +140,11 @@ resolved "https://registry.yarnpkg.com/@validatem/virtual-property/-/virtual-property-0.1.0.tgz#880540dfd149f98ecf1095d93912e34443381fe4" integrity sha512-JUUvWtdqoSkOwlsl20oB3qFHYIL05a/TAfdY4AJcs55QeVTiX5iI1b8IoQW644sIWWooBuLv+XwoxjRsQFczlQ== +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + as-expression@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/as-expression/-/as-expression-1.0.0.tgz#7bc620ca4cb2fe0ee90d86729bd6add33b8fd831" @@ -146,6 +155,13 @@ assure-array@^1.0.0: resolved "https://registry.yarnpkg.com/assure-array/-/assure-array-1.0.0.tgz#4f4ad16a87659d6200a4fb7103462033d216ec1f" integrity sha1-T0rRaodlnWIApPtxA0YgM9IW7B8= +clone-regexp@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f" + integrity sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q== + dependencies: + is-regexp "^2.0.0" + create-error@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/create-error/-/create-error-0.3.1.tgz#69810245a629e654432bf04377360003a5351a23" @@ -163,11 +179,28 @@ es6-promise-try@0.0.1: resolved "https://registry.yarnpkg.com/es6-promise-try/-/es6-promise-try-0.0.1.tgz#10f140dad27459cef949973e5d21a087f7274b20" integrity sha1-EPFA2tJ0Wc75SZc+XSGgh/cnSyA= +execall@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45" + integrity sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow== + dependencies: + clone-regexp "^2.1.0" + flatten@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + is-arguments@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" @@ -178,6 +211,11 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-regexp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d" + integrity sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA== + is-string@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" @@ -187,3 +225,17 @@ split-filter-n@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/split-filter-n/-/split-filter-n-1.1.2.tgz#268be1ec9c4d93dfb27b030c06165ac1b6f70f66" integrity sha512-+hXSQYpKe1uyXPXI4zQtAJAlaF2EzEc+BaF2goMeNL5oUD5YLqrVcpjxELJxpomXfwMCUaYLAszEbdY9gKVdHQ== + +supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + +syncpipe@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/syncpipe/-/syncpipe-1.0.0.tgz#170340f813150bc8fcb8878b1b9c71ea0ccd3727" + integrity sha512-cdiAFTnFJRvUaNPDc2n9CqoFvtIL3+JUMJZrC3kA3FzpugHOqu0TvkgNwmnxPZ5/WjAzMcfMS3xm+AO7rg/j/w== + dependencies: + assure-array "^1.0.0"