diff --git a/src/core/Dish.mjs b/src/core/Dish.mjs index af0fa32d..2276a36c 100755 --- a/src/core/Dish.mjs +++ b/src/core/Dish.mjs @@ -6,6 +6,7 @@ */ import Utils from "./Utils"; +import DishError from "./errors/DishError"; import BigNumber from "bignumber.js"; import log from "loglevel"; @@ -61,7 +62,7 @@ class Dish { case "list": return Dish.LIST_FILE; default: - throw "Invalid data type string. No matching enum."; + throw new DishError("Invalid data type string. No matching enum."); } } @@ -93,7 +94,7 @@ class Dish { case Dish.LIST_FILE: return "List"; default: - throw "Invalid data type enum. No matching type."; + throw new DishError("Invalid data type enum. No matching type."); } } @@ -117,7 +118,7 @@ class Dish { if (!this.valid()) { const sample = Utils.truncate(JSON.stringify(this.value), 13); - throw "Data is not a valid " + Dish.enumLookup(type) + ": " + sample; + throw new DishError(`Data is not a valid ${Dish.enumLookup(type)}: ${sample}`); } } @@ -151,77 +152,85 @@ class Dish { const byteArrayToStr = notUTF8 ? Utils.byteArrayToChars : Utils.byteArrayToUtf8; // Convert data to intermediate byteArray type - switch (this.type) { - case Dish.STRING: - this.value = this.value ? Utils.strToByteArray(this.value) : []; - break; - case Dish.NUMBER: - this.value = typeof this.value === "number" ? Utils.strToByteArray(this.value.toString()) : []; - break; - case Dish.HTML: - this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : []; - break; - case Dish.ARRAY_BUFFER: - // Array.from() would be nicer here, but it's slightly slower - this.value = Array.prototype.slice.call(new Uint8Array(this.value)); - break; - case Dish.BIG_NUMBER: - this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toFixed()) : []; - break; - case Dish.JSON: - this.value = this.value ? Utils.strToByteArray(JSON.stringify(this.value, null, 4)) : []; - break; - case Dish.FILE: - this.value = await Utils.readFile(this.value); - this.value = Array.prototype.slice.call(this.value); - break; - case Dish.LIST_FILE: - this.value = await Promise.all(this.value.map(async f => Utils.readFile(f))); - this.value = this.value.map(b => Array.prototype.slice.call(b)); - this.value = [].concat.apply([], this.value); - break; - default: - break; + try { + switch (this.type) { + case Dish.STRING: + this.value = this.value ? Utils.strToByteArray(this.value) : []; + break; + case Dish.NUMBER: + this.value = typeof this.value === "number" ? Utils.strToByteArray(this.value.toString()) : []; + break; + case Dish.HTML: + this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : []; + break; + case Dish.ARRAY_BUFFER: + // Array.from() would be nicer here, but it's slightly slower + this.value = Array.prototype.slice.call(new Uint8Array(this.value)); + break; + case Dish.BIG_NUMBER: + this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toFixed()) : []; + break; + case Dish.JSON: + this.value = this.value ? Utils.strToByteArray(JSON.stringify(this.value, null, 4)) : []; + break; + case Dish.FILE: + this.value = await Utils.readFile(this.value); + this.value = Array.prototype.slice.call(this.value); + break; + case Dish.LIST_FILE: + this.value = await Promise.all(this.value.map(async f => Utils.readFile(f))); + this.value = this.value.map(b => Array.prototype.slice.call(b)); + this.value = [].concat.apply([], this.value); + break; + default: + break; + } + } catch (err) { + throw new DishError(`Error translating from ${Dish.enumLookup(this.type)} to byteArray: ${err}`); } this.type = Dish.BYTE_ARRAY; // Convert from byteArray to toType - switch (toType) { - case Dish.STRING: - case Dish.HTML: - this.value = this.value ? byteArrayToStr(this.value) : ""; - this.type = Dish.STRING; - break; - case Dish.NUMBER: - this.value = this.value ? parseFloat(byteArrayToStr(this.value)) : 0; - this.type = Dish.NUMBER; - break; - case Dish.ARRAY_BUFFER: - this.value = new Uint8Array(this.value).buffer; - this.type = Dish.ARRAY_BUFFER; - break; - case Dish.BIG_NUMBER: - try { - this.value = new BigNumber(byteArrayToStr(this.value)); - } catch (err) { - this.value = new BigNumber(NaN); - } - this.type = Dish.BIG_NUMBER; - break; - case Dish.JSON: - this.value = JSON.parse(byteArrayToStr(this.value)); - this.type = Dish.JSON; - break; - case Dish.FILE: - this.value = new File(this.value, "unknown"); - break; - case Dish.LIST_FILE: - this.value = [new File(this.value, "unknown")]; - this.type = Dish.LIST_FILE; - break; - default: - break; + try { + switch (toType) { + case Dish.STRING: + case Dish.HTML: + this.value = this.value ? byteArrayToStr(this.value) : ""; + this.type = Dish.STRING; + break; + case Dish.NUMBER: + this.value = this.value ? parseFloat(byteArrayToStr(this.value)) : 0; + this.type = Dish.NUMBER; + break; + case Dish.ARRAY_BUFFER: + this.value = new Uint8Array(this.value).buffer; + this.type = Dish.ARRAY_BUFFER; + break; + case Dish.BIG_NUMBER: + try { + this.value = new BigNumber(byteArrayToStr(this.value)); + } catch (err) { + this.value = new BigNumber(NaN); + } + this.type = Dish.BIG_NUMBER; + break; + case Dish.JSON: + this.value = JSON.parse(byteArrayToStr(this.value)); + this.type = Dish.JSON; + break; + case Dish.FILE: + this.value = new File(this.value, "unknown"); + break; + case Dish.LIST_FILE: + this.value = [new File(this.value, "unknown")]; + this.type = Dish.LIST_FILE; + break; + default: + break; + } + } catch (err) { + throw new DishError(`Error translating from byteArray to ${Dish.enumLookup(toType)}: ${err}`); } } @@ -357,7 +366,7 @@ class Dish { ); break; default: - throw new Error("Cannot clone Dish, unknown type"); + throw new DishError("Cannot clone Dish, unknown type"); } return newDish; diff --git a/src/core/Recipe.mjs b/src/core/Recipe.mjs index a5b2e81f..b21f76b8 100755 --- a/src/core/Recipe.mjs +++ b/src/core/Recipe.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -// import Operation from "./Operation.js"; import OpModules from "./config/modules/OpModules"; import OperationConfig from "./config/OperationConfig.json"; import OperationError from "./errors/OperationError"; +import DishError from "./errors/DishError"; import log from "loglevel"; /** @@ -183,6 +183,10 @@ class Recipe { // native types is not fully supported yet. dish.set(err.message, "string"); return i; + } else if (err instanceof DishError || + (err.type && err.type === "DishError")) { + dish.set(err.message, "string"); + return i; } else { const e = typeof err == "string" ? { message: err } : err; diff --git a/src/core/errors/DishError.mjs b/src/core/errors/DishError.mjs new file mode 100644 index 00000000..922ebd4c --- /dev/null +++ b/src/core/errors/DishError.mjs @@ -0,0 +1,26 @@ +/** + * Custom error type for handling Dish type errors. + * i.e. where the Dish cannot be successfully translated between types + * + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +class DishError extends Error { + /** + * Standard error constructor. Adds no new behaviour. + * + * @param args - Standard error args + */ + constructor(...args) { + super(...args); + + this.type = "DishError"; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, DishError); + } + } +} + +export default DishError; diff --git a/src/core/operations/ToMessagePack.mjs b/src/core/operations/ToMessagePack.mjs index 40e31a41..ffcf3b43 100644 --- a/src/core/operations/ToMessagePack.mjs +++ b/src/core/operations/ToMessagePack.mjs @@ -5,7 +5,7 @@ */ import Operation from "../Operation"; -import OperationError from "../errors/OperationError.mjs"; +import OperationError from "../errors/OperationError"; import notepack from "notepack.io"; /** diff --git a/src/core/vendor/remove-exif.mjs b/src/core/vendor/remove-exif.mjs index 6c5c9e53..6377c674 100644 --- a/src/core/vendor/remove-exif.mjs +++ b/src/core/vendor/remove-exif.mjs @@ -18,7 +18,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import Utils from "../Utils.mjs"; +import Utils from "../Utils"; // Param jpeg should be a binaryArray export function removeEXIF(jpeg) { diff --git a/test/index.mjs b/test/index.mjs index 4a85a8a1..ff13fe3b 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -77,7 +77,7 @@ import "./tests/operations/SetUnion"; import "./tests/operations/StrUtils"; import "./tests/operations/SymmetricDifference"; import "./tests/operations/TextEncodingBruteForce"; -import "./tests/operations/ToGeohash.mjs"; +import "./tests/operations/ToGeohash"; import "./tests/operations/TranslateDateTimeFormat"; import "./tests/operations/Magic"; import "./tests/operations/ParseTLV"; @@ -155,7 +155,8 @@ TestRegister.runTests() } if (!allTestsPassing) { - console.log("\nNot all tests are passing"); + console.log("\nFailing tests:\n"); + results.filter(r => r.status !== "passing").forEach(handleTestResult); } process.exit(allTestsPassing ? 0 : 1); diff --git a/test/tests/operations/Code.mjs b/test/tests/operations/Code.mjs index 6d7cd102..4c716546 100644 --- a/test/tests/operations/Code.mjs +++ b/test/tests/operations/Code.mjs @@ -334,8 +334,8 @@ TestRegister.addTests([ }, { name: "To MessagePack: no content", - input: "", - expectedError: true, + input: "{}", + expectedMatch: /Unexpected end of JSON input/, recipeConfig: [ { "op": "To MessagePack",