Added support for personalization / Rewards Enrollment passes

This commit is contained in:
Alexander Cerutti
2019-07-27 00:41:48 +02:00
parent 2444d52cb2
commit eecfdb048f
2 changed files with 59 additions and 6 deletions

View File

@@ -2,10 +2,12 @@ import * as path from "path";
import forge from "node-forge";
import formatMessage from "./messages";
import { FactoryOptions, PartitionedBundle, BundleUnit, Certificates, FinalCertificates, isValid } from "./schema";
import { removeHidden, splitBufferBundle } from "./utils";
import { removeHidden, splitBufferBundle, getAllFilesWithName, hasFilesWithName, deletePersonalization } from "./utils";
import { promisify } from "util";
import { readFile as _readFile, readdir as _readdir } from "fs";
import debug from "debug";
const prsDebug = debug("Personalization");
const readDir = promisify(_readdir);
const readFile = promisify(_readFile);
@@ -38,9 +40,45 @@ export async function getModelContents(model: FactoryOptions["model"]) {
}
const modelFiles = Object.keys(modelContents.bundle);
const isModelInitialized = (
modelFiles.includes("pass.json") &&
hasFilesWithName("icon", modelFiles, "startsWith")
);
if (!(modelFiles.includes("pass.json") && modelContents.bundle["pass.json"].length && modelFiles.some(file => Boolean(file.includes("icon") && modelContents.bundle[file].length)))) {
throw new Error("missing icon or pass.json");
if (!isModelInitialized) {
// @TODO: set a good error message
throw new Error(formatMessage("MODEL_UNINITIALIZED", "parse result"));
}
// ======================= //
// *** Personalization *** //
// ======================= //
const personalizationJsonFile = "personalization.json";
if (!modelFiles.includes(personalizationJsonFile)) {
return modelContents;
}
const logoFullNames = getAllFilesWithName("personalizationLogo@", modelFiles, "startsWith");
if (!(logoFullNames.length && modelContents.bundle[personalizationJsonFile].length)) {
deletePersonalization(modelContents.bundle, logoFullNames);
return modelContents;
}
try {
const parsedPersonalization = JSON.parse(modelContents.bundle[personalizationJsonFile].toString("utf8"));
const isPersonalizationValid = isValid(parsedPersonalization, "personalizationDict");
if (!isPersonalizationValid) {
[...logoFullNames, personalizationJsonFile]
.forEach(file => delete modelContents.bundle[file]);
return modelContents;
}
} catch (err) {
prsDebug(formatMessage("PRS_INVALID", err));
deletePersonalization(modelContents.bundle, logoFullNames);
}
return modelContents;
@@ -63,7 +101,7 @@ export async function getModelFolderContents(model: string): Promise<Partitioned
const isModelInitialized = (
filteredFiles.length &&
filteredFiles.some(file => file.toLowerCase().includes("icon"))
hasFilesWithName("icon", filteredFiles, "startsWith")
);
// Icon is required to proceed
@@ -171,7 +209,7 @@ export function getModelBufferContents(model: BundleUnit): PartitionedBundle {
const isModelInitialized = (
bundleKeys.length &&
bundleKeys.some(file => file.toLowerCase().includes("icon"))
hasFilesWithName("icon", bundleKeys, "startsWith")
);
// Icon is required to proceed

View File

@@ -7,7 +7,7 @@ import { ZipFile } from "yazl";
import * as schema from "./schema";
import formatMessage from "./messages";
import FieldsArray from "./fieldsArray";
import { generateStringFile, dateToW3CString, isValidRGB } from "./utils";
import { generateStringFile, dateToW3CString, isValidRGB, deletePersonalization, getAllFilesWithName } from "./utils";
const barcodeDebug = debug("passkit:barcode");
const genericDebug = debug("passkit:generic");
@@ -134,6 +134,21 @@ export class Pass {
// Editing Pass.json
this.bundle["pass.json"] = this._patch(this.bundle["pass.json"]);
/**
* Checking Personalization, as this is available only with NFC
* @see https://apple.co/2SHfb22
*/
const currentBundleFiles = Object.keys(this.bundle);
if (!this[passProps].nfc && currentBundleFiles.includes("personalization.json")) {
genericDebug(formatMessage("PRS_REMOVED"));
deletePersonalization(this.bundle, getAllFilesWithName(
"personalizationLogo@",
currentBundleFiles,
"startsWith"
));
}
const finalBundle = { ...this.bundle } as schema.BundleUnit;
/**