mirror of
https://github.com/marcogll/passkit-generator.git
synced 2026-03-15 16:25:21 +00:00
Refactored Errors Messages to be references instead of looked up
This commit is contained in:
@@ -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),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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,7 +149,8 @@ export async function getModelFolderContents(
|
||||
|
||||
// Reading concurrently localizations folder
|
||||
// and their files and their buffers
|
||||
const L10N_FilesListByFolder: Array<Schemas.BundleUnit> = await Promise.all(
|
||||
const L10N_FilesListByFolder: Array<Schemas.BundleUnit> =
|
||||
await Promise.all(
|
||||
l10nFolders.map(async (folderPath) => {
|
||||
// Reading current folder
|
||||
const currentLangPath = path.join(modelPath, folderPath);
|
||||
@@ -185,7 +191,8 @@ export async function getModelFolderContents(
|
||||
}),
|
||||
);
|
||||
|
||||
const l10nBundle: Schemas.PartitionedBundle["l10nBundle"] = Object.assign(
|
||||
const l10nBundle: Schemas.PartitionedBundle["l10nBundle"] =
|
||||
Object.assign(
|
||||
{},
|
||||
...L10N_FilesListByFolder.map((folder, index) => ({
|
||||
[l10nFolders[index]]: folder,
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
36
src/pass.ts
36
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,11 +222,11 @@ export class Pass {
|
||||
this.l10nBundles[languageBundleDirname] = {};
|
||||
}
|
||||
|
||||
this.l10nBundles[languageBundleDirname]["pass.strings"] =
|
||||
Buffer.concat([
|
||||
this.l10nBundles[languageBundleDirname][
|
||||
"pass.strings"
|
||||
] = Buffer.concat([
|
||||
this.l10nBundles[languageBundleDirname]["pass.strings"] ||
|
||||
Buffer.alloc(0),
|
||||
] || Buffer.alloc(0),
|
||||
strings,
|
||||
]);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user