Refactored Errors Messages to be references instead of looked up

This commit is contained in:
Alexander Cerutti
2021-06-21 21:56:04 +02:00
parent 07321fba92
commit d35cb627e5
6 changed files with 105 additions and 105 deletions

View File

@@ -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),
);
});

View File

@@ -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));
}
}

View File

@@ -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);
}

View File

@@ -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 : "<passedValueIsUndefined>";
});
}
/**
* Looks among errors and debugMessages for the specified message name
* @param {string} name
*/
function resolveMessageName(name: AllMessages): string {
if (!errors[name] && !debugMessages[name]) {
return `<ErrorName "${name}" is not linked to any error messages>`;
}
return errors[name] || debugMessages[name];
}

View File

@@ -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<Schemas.BundleUnit> = await Promise.all(
l10nFolders.map(async (folderPath) => {
// Reading current folder
const currentLangPath = path.join(modelPath, folderPath);
const L10N_FilesListByFolder: Array<Schemas.BundleUnit> =
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<Schemas.BundleUnit>(
(acc, file, index) => {
if (!buffers[index].length) {
return acc;
}
return validFiles.reduce<Schemas.BundleUnit>(
(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),
);
}
}

View File

@@ -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;
}