mirror of
https://github.com/marcogll/passkit-generator.git
synced 2026-03-15 19:25:23 +00:00
Moved pass.json to typescript; Removed load method from pass implementation
This commit is contained in:
40
API.md
40
API.md
@@ -44,8 +44,6 @@ ___
|
|||||||
* [.relevance()](#method_relevance)
|
* [.relevance()](#method_relevance)
|
||||||
* Setting NFC
|
* Setting NFC
|
||||||
* [.nfc()](#method_nfc)
|
* [.nfc()](#method_nfc)
|
||||||
* Getting remote resources
|
|
||||||
* [.load()](#method_load)
|
|
||||||
* [Setting Pass Structure Keys (primaryFields, secondaryFields, ...)](#prop_fields)
|
* [Setting Pass Structure Keys (primaryFields, secondaryFields, ...)](#prop_fields)
|
||||||
* [TransitType](#prop_transitType)
|
* [TransitType](#prop_transitType)
|
||||||
* Generating the compiled pass.
|
* Generating the compiled pass.
|
||||||
@@ -402,44 +400,6 @@ ___
|
|||||||
**Getting remote resources**:
|
**Getting remote resources**:
|
||||||
___
|
___
|
||||||
|
|
||||||
<a name="method_load"></a>
|
|
||||||
|
|
||||||
#### .load()
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
pass.load(resource, name);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Returns**:
|
|
||||||
|
|
||||||
`Object<Pass> (this)`
|
|
||||||
|
|
||||||
**Description**:
|
|
||||||
|
|
||||||
Sets the resources to be downloaded in runtime to be pushed in the pass.
|
|
||||||
Use `name` param to give your downloaded file a name or to provide the folder path it will be pushed into (with the name, _obv._).
|
|
||||||
|
|
||||||
Requests are not cached and load method can only load pictures right now (no other types should be required). In case of conflict between downloaded files and model files, downloaded files will have the priority and will be putted in the zip file.
|
|
||||||
|
|
||||||
When in debug mode, file header is shown.
|
|
||||||
|
|
||||||
**Arguments**:
|
|
||||||
|
|
||||||
| Key | Type | Description | Optional | Default Value |
|
|
||||||
|-----|------|-------------|----------|:-------------:|
|
|
||||||
| resource | String | The URL where to fetch the picture | false | -
|
|
||||||
| name | String | The name / path to be used to call this | false | -
|
|
||||||
|
|
||||||
**Example**:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
pass.load("http://...", "icon.png");
|
|
||||||
pass.load("http://...", "en.lproj/icon.png");
|
|
||||||
```
|
|
||||||
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<a name="prop_fields"></a>
|
<a name="prop_fields"></a>
|
||||||
___
|
___
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ This package was created with a specific architecture in mind: **application** a
|
|||||||
|
|
||||||
Actually, pass creation and population doesn't fully happen within the application in runtime. Pass template is a folder in, for example, _your application directory_ (but nothing will stop you from putting it outside), that will contain all the objects needed (static medias) and structure to make a pass work.
|
Actually, pass creation and population doesn't fully happen within the application in runtime. Pass template is a folder in, for example, _your application directory_ (but nothing will stop you from putting it outside), that will contain all the objects needed (static medias) and structure to make a pass work.
|
||||||
|
|
||||||
Pass template will be read and pushed as is in the resulting .zip file along with web-fetched medias (also considered dynamic objects), while dynamic objects will be patched against `pass.json` or generated in runtime (`manifest.json`, `signature` and translation files).
|
Pass template will be read and pushed as is in the resulting .zip file, while dynamic objects will be patched against `pass.json` or generated in runtime (`manifest.json`, `signature` and translation files).
|
||||||
|
|
||||||
This package comes with an [API documentation](./API.md), that makes available a series of methods to customize passes.
|
This package comes with an [API documentation](./API.md), that makes available a series of methods to customize passes.
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"archiver": "^3.0.0",
|
"archiver": "^3.0.0",
|
||||||
"debug": "^3.2.6",
|
"debug": "^3.2.6",
|
||||||
"got": "^9.6.0",
|
|
||||||
"joi": "^13.7.0",
|
"joi": "^13.7.0",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"node-forge": "^0.7.6"
|
"node-forge": "^0.7.6"
|
||||||
@@ -32,7 +31,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/archiver": "^2.1.3",
|
"@types/archiver": "^2.1.3",
|
||||||
"@types/debug": "^4.1.4",
|
"@types/debug": "^4.1.4",
|
||||||
"@types/got": "^9.4.4",
|
|
||||||
"@types/joi": "^14.3.3",
|
"@types/joi": "^14.3.3",
|
||||||
"@types/node": "^12.0.0",
|
"@types/node": "^12.0.0",
|
||||||
"@types/node-forge": "^0.8.2",
|
"@types/node-forge": "^0.8.2",
|
||||||
|
|||||||
@@ -21,10 +21,7 @@ const debugMessages: MessageGroup = {
|
|||||||
BRC_FORMATTYPE_UNMATCH: "Format must be a string or null. Cannot set backward compatibility.",
|
BRC_FORMATTYPE_UNMATCH: "Format must be a string or null. Cannot set backward compatibility.",
|
||||||
BRC_AUTC_MISSING_DATA: "Unable to autogenerate barcodes. Data is not a string or an object with no message field",
|
BRC_AUTC_MISSING_DATA: "Unable to autogenerate barcodes. Data is not a string or an object with no message field",
|
||||||
BRC_BW_FORMAT_UNSUPPORTED: "This format is not supported (by Apple) for backward support. Please choose another one.",
|
BRC_BW_FORMAT_UNSUPPORTED: "This format is not supported (by Apple) for backward support. Please choose another one.",
|
||||||
DATE_FORMAT_UNMATCH: "%s was not set due to incorrect date format.",
|
DATE_FORMAT_UNMATCH: "%s was not set due to incorrect date format."
|
||||||
LOAD_TYPES_UNMATCH: "Resource and name are not valid strings. No action will be taken for the specified medias.",
|
|
||||||
LOAD_MIME: "Picture MIME-type: %s",
|
|
||||||
LOAD_NORES: "Was not able to fetch resource %s. Error: %s"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,24 +1,22 @@
|
|||||||
const fs = require("fs");
|
import fs from "fs";
|
||||||
const path = require("path");
|
import path from "path";
|
||||||
const { promisify } = require("util");
|
import { promisify } from "util";
|
||||||
const stream = require("stream");
|
import stream from "stream";
|
||||||
const forge = require("node-forge");
|
import forge from "node-forge";
|
||||||
const archiver = require("archiver");
|
import archiver from "archiver";
|
||||||
const debug = require("debug");
|
import debug from "debug";
|
||||||
const got = require("got");
|
|
||||||
|
|
||||||
const barcodeDebug = debug("passkit:barcode");
|
import * as schema from "./schema";
|
||||||
const genericDebug = debug("passkit:generic");
|
import formatMessage from "./messages";
|
||||||
const loadDebug = debug("passkit:load");
|
import FieldsArray from "./fieldsArray";
|
||||||
|
import {
|
||||||
const schema = require("./schema");
|
|
||||||
const formatMessage = require("./messages");
|
|
||||||
const FieldsArray = require("./fieldsArray");
|
|
||||||
const {
|
|
||||||
assignLength, generateStringFile,
|
assignLength, generateStringFile,
|
||||||
removeHidden, dateToW3CString,
|
removeHidden, dateToW3CString,
|
||||||
isValidRGB
|
isValidRGB
|
||||||
} = require("./utils");
|
} from "./utils";
|
||||||
|
|
||||||
|
const barcodeDebug = debug("passkit:barcode");
|
||||||
|
const genericDebug = debug("passkit:generic");
|
||||||
|
|
||||||
const readdir = promisify(fs.readdir);
|
const readdir = promisify(fs.readdir);
|
||||||
const readFile = promisify(fs.readFile);
|
const readFile = promisify(fs.readFile);
|
||||||
@@ -28,8 +26,23 @@ const transitType = Symbol("transitType");
|
|||||||
const barcodesFillMissing = Symbol("bfm");
|
const barcodesFillMissing = Symbol("bfm");
|
||||||
const barcodesSetBackward = Symbol("bsb");
|
const barcodesSetBackward = Symbol("bsb");
|
||||||
|
|
||||||
class Pass {
|
interface PassIndexSignature {
|
||||||
constructor(options) {
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Pass implements PassIndexSignature {
|
||||||
|
private model: string;
|
||||||
|
private _fields: string[];
|
||||||
|
private _props: { [key: string]: any };
|
||||||
|
private type: string;
|
||||||
|
private fieldsKeys: Set<string>;
|
||||||
|
|
||||||
|
Certificates: schema.Certificates;
|
||||||
|
l10n: { [key: string]: { [key: string]: string } } = {};
|
||||||
|
shouldOverwrite: boolean;
|
||||||
|
[transitType]: string = "";
|
||||||
|
|
||||||
|
constructor(options: schema.PassInstance) {
|
||||||
this.Certificates = {
|
this.Certificates = {
|
||||||
// Even if this assigning will fail, it will be captured below
|
// Even if this assigning will fail, it will be captured below
|
||||||
// in _parseSettings, since this won't match with the schema.
|
// in _parseSettings, since this won't match with the schema.
|
||||||
@@ -38,8 +51,6 @@ class Pass {
|
|||||||
|
|
||||||
options.overrides = options.overrides || {};
|
options.overrides = options.overrides || {};
|
||||||
|
|
||||||
this.l10n = {};
|
|
||||||
this._remoteResources = [];
|
|
||||||
this.shouldOverwrite = !(options.hasOwnProperty("shouldOverwrite") && !options.shouldOverwrite);
|
this.shouldOverwrite = !(options.hasOwnProperty("shouldOverwrite") && !options.shouldOverwrite);
|
||||||
|
|
||||||
this._fields = ["primaryFields", "secondaryFields", "auxiliaryFields", "backFields", "headerFields"];
|
this._fields = ["primaryFields", "secondaryFields", "auxiliaryFields", "backFields", "headerFields"];
|
||||||
@@ -69,37 +80,10 @@ class Pass {
|
|||||||
// Reading the model
|
// Reading the model
|
||||||
const modelFilesList = await readdir(this.model);
|
const modelFilesList = await readdir(this.model);
|
||||||
|
|
||||||
/**
|
|
||||||
* Getting the buffers for remote files
|
|
||||||
*/
|
|
||||||
|
|
||||||
const buffersPromise = await this._remoteResources.reduce(async (acc, current) => {
|
|
||||||
try {
|
|
||||||
const response = await got(current[0], { encoding: null });
|
|
||||||
loadDebug(formatMessage("LOAD_MIME", response.headers["content-type"]));
|
|
||||||
|
|
||||||
if (!Buffer.isBuffer(response.body)) {
|
|
||||||
throw "LOADED_RESOURCE_NOT_A_BUFFER";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.headers["content-type"].includes("image/")) {
|
|
||||||
throw "LOADED_RESOURCE_NOT_A_PICTURE";
|
|
||||||
}
|
|
||||||
|
|
||||||
return [...acc, response.body];
|
|
||||||
} catch (err) {
|
|
||||||
loadDebug(formatMessage("LOAD_NORES", current[1], err));
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const remoteFilesList = buffersPromise.length ? this._remoteResources.map(r => r[1]): [];
|
|
||||||
|
|
||||||
// list without dynamic components like manifest, signature or pass files (will be added later in the flow) and hidden files.
|
// list without dynamic components like manifest, signature or pass files (will be added later in the flow) and hidden files.
|
||||||
const noDynList = removeHidden(modelFilesList).filter(f => !/(manifest|signature|pass)/i.test(f));
|
const noDynList = removeHidden(modelFilesList).filter(f => !/(manifest|signature|pass)/i.test(f));
|
||||||
const hasAssets = noDynList.length || remoteFilesList.length;
|
|
||||||
|
|
||||||
if (!hasAssets || ![...noDynList, ...remoteFilesList].some(f => f.toLowerCase().includes("icon"))) {
|
if (!noDynList.length || !noDynList.some(f => f.toLowerCase().includes("icon"))) {
|
||||||
let eMessage = formatMessage("MODEL_UNINITIALIZED", path.parse(this.model).name);
|
let eMessage = formatMessage("MODEL_UNINITIALIZED", path.parse(this.model).name);
|
||||||
throw new Error(eMessage);
|
throw new Error(eMessage);
|
||||||
}
|
}
|
||||||
@@ -132,12 +116,6 @@ class Pass {
|
|||||||
|
|
||||||
/* Getting all bundle file buffers, pass.json included, and appending path */
|
/* Getting all bundle file buffers, pass.json included, and appending path */
|
||||||
|
|
||||||
if (remoteFilesList.length) {
|
|
||||||
// Removing files in bundle that also exist in remoteFilesList
|
|
||||||
// I'm giving priority to downloaded files
|
|
||||||
bundle = bundle.filter(file => !remoteFilesList.includes(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reading bundle files to buffers without pass.json - it gets read below
|
// Reading bundle files to buffers without pass.json - it gets read below
|
||||||
// to use a different parsing process
|
// to use a different parsing process
|
||||||
|
|
||||||
@@ -145,7 +123,7 @@ class Pass {
|
|||||||
const passBuffer = this._extractPassDefinition();
|
const passBuffer = this._extractPassDefinition();
|
||||||
bundle.push("pass.json");
|
bundle.push("pass.json");
|
||||||
|
|
||||||
const buffers = await Promise.all([...bundleBuffers, passBuffer, ...buffersPromise]);
|
const buffers = await Promise.all([...bundleBuffers, passBuffer]);
|
||||||
|
|
||||||
Object.keys(this.l10n).forEach(lang => {
|
Object.keys(this.l10n).forEach(lang => {
|
||||||
const strings = generateStringFile(this.l10n[lang]);
|
const strings = generateStringFile(this.l10n[lang]);
|
||||||
@@ -183,9 +161,6 @@ class Pass {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pushing the remote files into the bundle
|
|
||||||
bundle.push(...remoteFilesList);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parsing the buffers, pushing them into the archive
|
* Parsing the buffers, pushing them into the archive
|
||||||
* and returning the compiled manifest
|
* and returning the compiled manifest
|
||||||
@@ -506,23 +481,6 @@ class Pass {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a web resource (image)
|
|
||||||
* @param {string} resource
|
|
||||||
* @param {string} name
|
|
||||||
*/
|
|
||||||
|
|
||||||
load(resource, name) {
|
|
||||||
if (typeof resource !== "string" && typeof name !== "string") {
|
|
||||||
loadDebug(formatMessage("LOAD_TYPES_UNMATCH"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._remoteResources.push([resource, name]);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if pass model type is one of the supported ones
|
* Checks if pass model type is one of the supported ones
|
||||||
*
|
*
|
||||||
@@ -589,8 +547,9 @@ class Pass {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
signature.addSigner({
|
signature.addSigner({
|
||||||
key: this.Certificates.signerKey,
|
key: this.Certificates.signerKey.keyFile,
|
||||||
certificate: this.Certificates.signerCert,
|
certificate: this.Certificates.signerCert,
|
||||||
|
digestAlgorithm: forge.pki.oids.sha1,
|
||||||
authenticatedAttributes: [{
|
authenticatedAttributes: [{
|
||||||
type: forge.pki.oids.contentType,
|
type: forge.pki.oids.contentType,
|
||||||
value: forge.pki.oids.data
|
value: forge.pki.oids.data
|
||||||
@@ -731,7 +690,7 @@ class Pass {
|
|||||||
* @returns {Object} - parsed certificates to be pushed to Pass.Certificates.
|
* @returns {Object} - parsed certificates to be pushed to Pass.Certificates.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function readCertificates(certificates) {
|
function readCertificates(certificates: schema.Certificates) {
|
||||||
if (certificates.wwdr && certificates.signerCert && typeof certificates.signerKey === "object") {
|
if (certificates.wwdr && certificates.signerCert && typeof certificates.signerKey === "object") {
|
||||||
// Nothing must be added. Void object is returned.
|
// Nothing must be added. Void object is returned.
|
||||||
return Promise.resolve({});
|
return Promise.resolve({});
|
||||||
@@ -740,14 +699,14 @@ function readCertificates(certificates) {
|
|||||||
const raw = certificates._raw;
|
const raw = certificates._raw;
|
||||||
const optCertsNames = Object.keys(raw);
|
const optCertsNames = Object.keys(raw);
|
||||||
const certPaths = optCertsNames.map((val) => {
|
const certPaths = optCertsNames.map((val) => {
|
||||||
const cert = raw[val];
|
const cert: string | typeof certificates.signerKey = raw[val];
|
||||||
// realRawValue exists as signerKey might be an object
|
// realRawValue exists as signerKey might be an object
|
||||||
const realRawValue = !(cert instanceof Object) ? cert : cert["keyFile"];
|
const realRawValue = !(cert instanceof Object) ? cert : cert["keyFile"];
|
||||||
|
|
||||||
// We are checking if the string is a path or a content
|
// We are checking if the string is a path or a content
|
||||||
if (!!path.parse(realRawValue).ext) {
|
if (!!path.parse(realRawValue).ext) {
|
||||||
const resolvedPath = path.resolve(realRawValue);
|
const resolvedPath = path.resolve(realRawValue);
|
||||||
return readFile(resolvedPath);
|
return readFile(resolvedPath, { encoding: "utf8" });
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve(realRawValue);
|
return Promise.resolve(realRawValue);
|
||||||
}
|
}
|
||||||
@@ -759,6 +718,7 @@ function readCertificates(certificates) {
|
|||||||
// which is conjoint later with the other pems
|
// which is conjoint later with the other pems
|
||||||
|
|
||||||
return Object.assign(
|
return Object.assign(
|
||||||
|
{},
|
||||||
...contents.map((file, index) => {
|
...contents.map((file, index) => {
|
||||||
const certName = optCertsNames[index];
|
const certName = optCertsNames[index];
|
||||||
const pem = parsePEM(certName, file, raw[certName].passphrase);
|
const pem = parsePEM(certName, file, raw[certName].passphrase);
|
||||||
@@ -825,5 +785,3 @@ function barcodesFromUncompleteData(origin) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { Pass };
|
|
||||||
Reference in New Issue
Block a user