From d35cb627e562a05ae9d6a4267f443ca3c2498a48 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Mon, 21 Jun 2021 21:56:04 +0200 Subject: [PATCH] Refactored Errors Messages to be references instead of looked up --- spec/factory.ts | 4 +- src/abstract.ts | 6 +-- src/factory.ts | 11 +++-- src/messages.ts | 34 ++++++--------- src/parser.ts | 111 ++++++++++++++++++++++++++---------------------- src/pass.ts | 44 +++++++++---------- 6 files changed, 105 insertions(+), 105 deletions(-) diff --git a/spec/factory.ts b/spec/factory.ts index b41a07f..1d1bba9 100644 --- a/spec/factory.ts +++ b/spec/factory.ts @@ -1,12 +1,12 @@ import { createPass } from "../lib/factory"; -import formatMessage from "../lib/messages"; +import formatMessage, { ERROR } from "../lib/messages"; import * as fs from "fs"; import * as path from "path"; describe("createPass", () => { it("should throw if first argument is not provided", async () => { await expectAsync(createPass(undefined)).toBeRejectedWithError( - formatMessage("CP_NO_OPTS"), + formatMessage(ERROR.CP_NO_OPTS), ); }); diff --git a/src/abstract.ts b/src/abstract.ts index 96d2982..3e98c7f 100644 --- a/src/abstract.ts +++ b/src/abstract.ts @@ -1,6 +1,6 @@ import * as Schemas from "./schemas"; import { getModelContents, readCertificatesFromOptions } from "./parser"; -import formatMessage from "./messages"; +import formatMessage, { ERROR } from "./messages"; const abmCertificates = Symbol("certificates"); const abmModel = Symbol("model"); @@ -25,7 +25,7 @@ interface AbstractModelOptions { export async function createAbstractModel(options: AbstractFactoryOptions) { if (!(options && Object.keys(options).length)) { - throw new Error(formatMessage("CP_NO_OPTS")); + throw new Error(formatMessage(ERROR.CP_NO_OPTS)); } try { @@ -40,7 +40,7 @@ export async function createAbstractModel(options: AbstractFactoryOptions) { overrides: options.overrides, }); } catch (err) { - throw new Error(formatMessage("CP_INIT_ERROR", "abstract model", err)); + throw new Error(formatMessage(ERROR.CP_INIT, "abstract model", err)); } } diff --git a/src/factory.ts b/src/factory.ts index 2646806..e88c8aa 100644 --- a/src/factory.ts +++ b/src/factory.ts @@ -1,6 +1,6 @@ import { Pass } from "./pass"; import * as Schemas from "./schemas"; -import formatMessage from "./messages"; +import formatMessage, { ERROR } from "./messages"; import { getModelContents, readCertificatesFromOptions } from "./parser"; import { splitBufferBundle } from "./utils"; import { AbstractModel, AbstractFactoryOptions } from "./abstract"; @@ -24,7 +24,7 @@ export async function createPass( (options instanceof AbstractModel || Object.keys(options).length) ) ) { - throw new Error(formatMessage("CP_NO_OPTS")); + throw new Error(formatMessage(ERROR.CP_NO_OPTS)); } try { @@ -74,7 +74,7 @@ export async function createPass( ); } } catch (err) { - throw new Error(formatMessage("CP_INIT_ERROR", "pass", err)); + throw new Error(formatMessage(ERROR.CP_INIT, "pass", err)); } } @@ -85,9 +85,8 @@ function createPassInstance( additionalBuffers?: Schemas.BundleUnit, ) { if (additionalBuffers) { - const [additionalL10n, additionalBundle] = splitBufferBundle( - additionalBuffers, - ); + const [additionalL10n, additionalBundle] = + splitBufferBundle(additionalBuffers); Object.assign(model["l10nBundle"], additionalL10n); Object.assign(model["bundle"], additionalBundle); } diff --git a/src/messages.ts b/src/messages.ts index 7d0d227..a14692a 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -1,5 +1,5 @@ -const errors = { - CP_INIT_ERROR: +export const ERROR = { + CP_INIT: "Something went really bad in the %s initialization! Look at the log below this message. It should contain all the infos about the problem: \n%s", CP_NO_OPTS: "Cannot initialize the pass or abstract model creation: no options were passed.", @@ -24,9 +24,9 @@ const errors = { "Cannot proceed with pass creation due to bad keys format in overrides.", NO_PASS_TYPE: "Cannot proceed with pass creation. Model definition (pass.json) has no valid type in it.\nRefer to https://apple.co/2wzyL5J to choose a valid pass type.", -}; +} as const; -const debugMessages = { +export const DEBUG = { TRSTYPE_NOT_VALID: 'Transit type changing rejected as not compliant with Apple Specifications. Transit type would become "%s" but should be in [PKTransitTypeAir, PKTransitTypeBoat, PKTransitTypeBus, PKTransitTypeGeneric, PKTransitTypeTrain]', BRC_NOT_SUPPORTED: @@ -46,9 +46,11 @@ const debugMessages = { "Unable to parse Personalization.json. File is not a valid JSON. Error: %s", PRS_REMOVED: "Personalization has been removed as it requires an NFC-enabled pass to work.", -}; +} as const; -type AllMessages = keyof (typeof debugMessages & typeof errors); +type ERROR_OR_DEBUG_MESSAGE = + | typeof ERROR[keyof typeof ERROR] + | typeof DEBUG[keyof typeof DEBUG]; /** * Creates a message with replaced values @@ -56,24 +58,14 @@ type AllMessages = keyof (typeof debugMessages & typeof errors); * @param {any[]} values */ -export default function format(messageName: AllMessages, ...values: any[]) { +export default function format( + messageName: ERROR_OR_DEBUG_MESSAGE, + ...values: any[] +) { // reversing because it is better popping than shifting. let replaceValues = values.reverse(); - return resolveMessageName(messageName).replace(/%s/g, () => { + return messageName.replace(/%s/g, () => { let next = replaceValues.pop(); return next !== undefined ? next : ""; }); } - -/** - * Looks among errors and debugMessages for the specified message name - * @param {string} name - */ - -function resolveMessageName(name: AllMessages): string { - if (!errors[name] && !debugMessages[name]) { - return ``; - } - - return errors[name] || debugMessages[name]; -} diff --git a/src/parser.ts b/src/parser.ts index e979472..05d2ee2 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -1,6 +1,6 @@ import * as path from "path"; import forge from "node-forge"; -import formatMessage from "./messages"; +import formatMessage, { ERROR, DEBUG } from "./messages"; import * as Schemas from "./schemas"; import { removeHidden, @@ -29,7 +29,7 @@ export async function getModelContents(model: Schemas.FactoryOptions["model"]) { } else if (typeof model === "object" && Object.keys(model).length) { modelContents = getModelBufferContents(model); } else { - throw new Error(formatMessage("MODEL_NOT_VALID")); + throw new Error(formatMessage(ERROR.MODEL_NOT_VALID)); } const modelFiles = Object.keys(modelContents.bundle); @@ -38,7 +38,9 @@ export async function getModelContents(model: Schemas.FactoryOptions["model"]) { hasFilesWithName("icon", modelFiles, "startsWith"); if (!isModelInitialized) { - throw new Error(formatMessage("MODEL_UNINITIALIZED", "parse result")); + throw new Error( + formatMessage(ERROR.MODEL_UNINITIALIZED, "parse result"), + ); } // ======================= // @@ -83,7 +85,7 @@ export async function getModelContents(model: Schemas.FactoryOptions["model"]) { return modelContents; } } catch (err) { - prsDebug(formatMessage("PRS_INVALID", err)); + prsDebug(formatMessage(DEBUG.PRS_INVALID, err)); deletePersonalization(modelContents.bundle, logoFullNames); } @@ -117,7 +119,10 @@ export async function getModelFolderContents( // Icon is required to proceed if (!isModelInitialized) { throw new Error( - formatMessage("MODEL_UNINITIALIZED", path.parse(model).name), + formatMessage( + ERROR.MODEL_UNINITIALIZED, + path.parse(model).name, + ), ); } @@ -144,53 +149,55 @@ export async function getModelFolderContents( // Reading concurrently localizations folder // and their files and their buffers - const L10N_FilesListByFolder: Array = await Promise.all( - l10nFolders.map(async (folderPath) => { - // Reading current folder - const currentLangPath = path.join(modelPath, folderPath); + const L10N_FilesListByFolder: Array = + await Promise.all( + l10nFolders.map(async (folderPath) => { + // Reading current folder + const currentLangPath = path.join(modelPath, folderPath); - const files = await readDir(currentLangPath); - // Transforming files path to a model-relative path - const validFiles = removeHidden(files).map((file) => - path.join(currentLangPath, file), - ); + const files = await readDir(currentLangPath); + // Transforming files path to a model-relative path + const validFiles = removeHidden(files).map((file) => + path.join(currentLangPath, file), + ); - // Getting all the buffers from file paths - const buffers = await Promise.all( - validFiles.map((file) => - readFile(file).catch(() => Buffer.alloc(0)), - ), - ); + // Getting all the buffers from file paths + const buffers = await Promise.all( + validFiles.map((file) => + readFile(file).catch(() => Buffer.alloc(0)), + ), + ); - // Assigning each file path to its buffer - // and discarding the empty ones + // Assigning each file path to its buffer + // and discarding the empty ones - return validFiles.reduce( - (acc, file, index) => { - if (!buffers[index].length) { - return acc; - } + return validFiles.reduce( + (acc, file, index) => { + if (!buffers[index].length) { + return acc; + } - const fileComponents = file.split(path.sep); - const fileName = - fileComponents[fileComponents.length - 1]; + const fileComponents = file.split(path.sep); + const fileName = + fileComponents[fileComponents.length - 1]; - return { - ...acc, - [fileName]: buffers[index], - }; - }, - {}, - ); - }), - ); + return { + ...acc, + [fileName]: buffers[index], + }; + }, + {}, + ); + }), + ); - const l10nBundle: Schemas.PartitionedBundle["l10nBundle"] = Object.assign( - {}, - ...L10N_FilesListByFolder.map((folder, index) => ({ - [l10nFolders[index]]: folder, - })), - ); + const l10nBundle: Schemas.PartitionedBundle["l10nBundle"] = + Object.assign( + {}, + ...L10N_FilesListByFolder.map((folder, index) => ({ + [l10nFolders[index]]: folder, + })), + ); return { bundle, @@ -200,13 +207,15 @@ export async function getModelFolderContents( if (err?.code === "ENOENT") { if (err.syscall === "open") { // file opening failed - throw new Error(formatMessage("MODELF_NOT_FOUND", err.path)); + throw new Error( + formatMessage(ERROR.MODELF_NOT_FOUND, err.path), + ); } else if (err.syscall === "scandir") { // directory reading failed const pathContents = (err.path as string).split(/(\/|\\\?)/); throw new Error( formatMessage( - "MODELF_FILE_NOT_FOUND", + ERROR.MODELF_FILE_NOT_FOUND, pathContents[pathContents.length - 1], ), ); @@ -246,7 +255,7 @@ export function getModelBufferContents( // Icon is required to proceed if (!isModelInitialized) { - throw new Error(formatMessage("MODEL_UNINITIALIZED", "Buffers")); + throw new Error(formatMessage(ERROR.MODEL_UNINITIALIZED, "Buffers")); } // separing localization folders from bundle files @@ -278,7 +287,7 @@ export async function readCertificatesFromOptions( Schemas.isValid(options, Schemas.CertificatesSchema) ) ) { - throw new Error(formatMessage("CP_NO_CERTS")); + throw new Error(formatMessage(ERROR.CP_NO_CERTS)); } let signerKey: string; @@ -320,7 +329,7 @@ export async function readCertificatesFromOptions( const pem = parsePEM(certName, file, passphrase); if (!pem) { - throw new Error(formatMessage("INVALID_CERTS", certName)); + throw new Error(formatMessage(ERROR.INVALID_CERTS, certName)); } return { [certName]: pem }; @@ -333,7 +342,7 @@ export async function readCertificatesFromOptions( } throw new Error( - formatMessage("INVALID_CERT_PATH", path.parse(err.path).base), + formatMessage(ERROR.INVALID_CERT_PATH, path.parse(err.path).base), ); } } diff --git a/src/pass.ts b/src/pass.ts index e4e2363..688017c 100644 --- a/src/pass.ts +++ b/src/pass.ts @@ -6,7 +6,7 @@ import { ZipFile } from "yazl"; import type Joi from "joi"; import * as Schemas from "./schemas"; -import formatMessage from "./messages"; +import formatMessage, { ERROR, DEBUG } from "./messages"; import FieldsArray from "./fieldsArray"; import { generateStringFile, @@ -56,7 +56,7 @@ export class Pass { constructor(options: Schemas.PassInstance) { if (!Schemas.isValid(options, Schemas.PassInstance)) { - throw new Error(formatMessage("REQUIR_VALID_FAILED")); + throw new Error(formatMessage(ERROR.REQUIR_VALID_FAILED)); } this.Certificates = options.certificates; @@ -68,7 +68,7 @@ export class Pass { this.bundle["pass.json"].toString("utf8"), ); } catch (err) { - throw new Error(formatMessage("PASSFILE_VALIDATION_FAILED")); + throw new Error(formatMessage(ERROR.PASSFILE_VALIDATION_FAILED)); } // Parsing the options and extracting only the valid ones. @@ -78,7 +78,7 @@ export class Pass { ); if (validOverrides === null) { - throw new Error(formatMessage("OVV_KEYS_BADFORMAT")); + throw new Error(formatMessage(ERROR.OVV_KEYS_BADFORMAT)); } this.type = Object.keys(this.passCore).find((key) => @@ -86,7 +86,7 @@ export class Pass { ) as keyof Schemas.ValidPassType; if (!this.type) { - throw new Error(formatMessage("NO_PASS_TYPE")); + throw new Error(formatMessage(ERROR.NO_PASS_TYPE)); } // Parsing and validating pass.json keys @@ -184,7 +184,7 @@ export class Pass { !this[passProps].nfc && currentBundleFiles.includes("personalization.json") ) { - genericDebug(formatMessage("PRS_REMOVED")); + genericDebug(formatMessage(DEBUG.PRS_REMOVED)); deletePersonalization( this.bundle, getAllFilesWithName( @@ -222,13 +222,13 @@ export class Pass { this.l10nBundles[languageBundleDirname] = {}; } - this.l10nBundles[languageBundleDirname][ - "pass.strings" - ] = Buffer.concat([ - this.l10nBundles[languageBundleDirname]["pass.strings"] || - Buffer.alloc(0), - strings, - ]); + this.l10nBundles[languageBundleDirname]["pass.strings"] = + Buffer.concat([ + this.l10nBundles[languageBundleDirname][ + "pass.strings" + ] || Buffer.alloc(0), + strings, + ]); } if ( @@ -453,7 +453,7 @@ export class Pass { const autogen = barcodesFromUncompleteData(data[0]); if (!autogen.length) { - barcodeDebug(formatMessage("BRC_AUTC_MISSING_DATA")); + barcodeDebug(formatMessage(DEBUG.BRC_AUTC_MISSING_DATA)); return this; } @@ -520,17 +520,17 @@ export class Pass { } if (typeof chosenFormat !== "string") { - barcodeDebug(formatMessage("BRC_FORMATTYPE_UNMATCH")); + barcodeDebug(formatMessage(DEBUG.BRC_FORMATTYPE_UNMATCH)); return this; } if (chosenFormat === "PKBarcodeFormatCode128") { - barcodeDebug(formatMessage("BRC_BW_FORMAT_UNSUPPORTED")); + barcodeDebug(formatMessage(DEBUG.BRC_BW_FORMAT_UNSUPPORTED)); return this; } if (!(barcodes && barcodes.length)) { - barcodeDebug(formatMessage("BRC_NO_POOL")); + barcodeDebug(formatMessage(DEBUG.BRC_NO_POOL)); return this; } @@ -540,7 +540,7 @@ export class Pass { ); if (index === -1) { - barcodeDebug(formatMessage("BRC_NOT_SUPPORTED")); + barcodeDebug(formatMessage(DEBUG.BRC_NOT_SUPPORTED)); return this; } @@ -571,7 +571,7 @@ export class Pass { Schemas.isValid(data, Schemas.NFC) ) ) { - genericDebug(formatMessage("NFC_INVALID")); + genericDebug(formatMessage(DEBUG.NFC_INVALID)); return this; } @@ -633,7 +633,7 @@ export class Pass { }); if (this.type === "boardingPass" && !this[transitType]) { - throw new Error(formatMessage("TRSTYPE_REQUIRED")); + throw new Error(formatMessage(ERROR.TRSTYPE_REQUIRED)); } passFile[this.type]["transitType"] = this[transitType]; @@ -643,7 +643,7 @@ export class Pass { set transitType(value: string) { if (!Schemas.isValid(value, Schemas.TransitType)) { - genericDebug(formatMessage("TRSTYPE_NOT_VALID", value)); + genericDebug(formatMessage(DEBUG.TRSTYPE_NOT_VALID, value)); this[transitType] = this[transitType] || ""; return; } @@ -701,7 +701,7 @@ function processDate(key: string, date: Date): string | null { const dateParse = dateToW3CString(date); if (!dateParse) { - genericDebug(formatMessage("DATE_FORMAT_UNMATCH", key)); + genericDebug(formatMessage(DEBUG.DATE_FORMAT_UNMATCH, key)); return null; }