From 9609187fa5af66dc17c5642a93eca1b6bbabbf5c Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Mon, 20 May 2019 22:08:08 +0200 Subject: [PATCH] Moved pass.json to typescript; Removed load method from pass implementation --- API.md | 40 ------------- README.md | 2 +- package.json | 2 - src/messages.ts | 5 +- src/{pass.js => pass.ts} | 124 +++++++++++++-------------------------- 5 files changed, 43 insertions(+), 130 deletions(-) rename src/{pass.js => pass.ts} (90%) diff --git a/API.md b/API.md index 5cddabd..a65d555 100644 --- a/API.md +++ b/API.md @@ -44,8 +44,6 @@ ___ * [.relevance()](#method_relevance) * Setting NFC * [.nfc()](#method_nfc) - * Getting remote resources - * [.load()](#method_load) * [Setting Pass Structure Keys (primaryFields, secondaryFields, ...)](#prop_fields) * [TransitType](#prop_transitType) * Generating the compiled pass. @@ -402,44 +400,6 @@ ___ **Getting remote resources**: ___ - - -#### .load() - -```javascript -pass.load(resource, name); -``` - -**Returns**: - -`Object (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"); -``` - -
-
- ___ diff --git a/README.md b/README.md index 826b3e6..5597c45 100644 --- a/README.md +++ b/README.md @@ -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. -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. diff --git a/package.json b/package.json index d2f123f..6fa9a70 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "dependencies": { "archiver": "^3.0.0", "debug": "^3.2.6", - "got": "^9.6.0", "joi": "^13.7.0", "moment": "^2.24.0", "node-forge": "^0.7.6" @@ -32,7 +31,6 @@ "devDependencies": { "@types/archiver": "^2.1.3", "@types/debug": "^4.1.4", - "@types/got": "^9.4.4", "@types/joi": "^14.3.3", "@types/node": "^12.0.0", "@types/node-forge": "^0.8.2", diff --git a/src/messages.ts b/src/messages.ts index 6fa1c95..c70e66f 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -21,10 +21,7 @@ const debugMessages: MessageGroup = { 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_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.", - 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" + DATE_FORMAT_UNMATCH: "%s was not set due to incorrect date format." }; /** diff --git a/src/pass.js b/src/pass.ts similarity index 90% rename from src/pass.js rename to src/pass.ts index 4dc410a..382e38e 100644 --- a/src/pass.js +++ b/src/pass.ts @@ -1,24 +1,22 @@ -const fs = require("fs"); -const path = require("path"); -const { promisify } = require("util"); -const stream = require("stream"); -const forge = require("node-forge"); -const archiver = require("archiver"); -const debug = require("debug"); -const got = require("got"); +import fs from "fs"; +import path from "path"; +import { promisify } from "util"; +import stream from "stream"; +import forge from "node-forge"; +import archiver from "archiver"; +import debug from "debug"; -const barcodeDebug = debug("passkit:barcode"); -const genericDebug = debug("passkit:generic"); -const loadDebug = debug("passkit:load"); - -const schema = require("./schema"); -const formatMessage = require("./messages"); -const FieldsArray = require("./fieldsArray"); -const { +import * as schema from "./schema"; +import formatMessage from "./messages"; +import FieldsArray from "./fieldsArray"; +import { assignLength, generateStringFile, removeHidden, dateToW3CString, isValidRGB -} = require("./utils"); +} from "./utils"; + +const barcodeDebug = debug("passkit:barcode"); +const genericDebug = debug("passkit:generic"); const readdir = promisify(fs.readdir); const readFile = promisify(fs.readFile); @@ -28,8 +26,23 @@ const transitType = Symbol("transitType"); const barcodesFillMissing = Symbol("bfm"); const barcodesSetBackward = Symbol("bsb"); -class Pass { - constructor(options) { +interface PassIndexSignature { + [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; + + Certificates: schema.Certificates; + l10n: { [key: string]: { [key: string]: string } } = {}; + shouldOverwrite: boolean; + [transitType]: string = ""; + + constructor(options: schema.PassInstance) { this.Certificates = { // Even if this assigning will fail, it will be captured below // in _parseSettings, since this won't match with the schema. @@ -38,8 +51,6 @@ class Pass { options.overrides = options.overrides || {}; - this.l10n = {}; - this._remoteResources = []; this.shouldOverwrite = !(options.hasOwnProperty("shouldOverwrite") && !options.shouldOverwrite); this._fields = ["primaryFields", "secondaryFields", "auxiliaryFields", "backFields", "headerFields"]; @@ -69,37 +80,10 @@ class Pass { // Reading the 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. 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); throw new Error(eMessage); } @@ -132,12 +116,6 @@ class Pass { /* 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 // to use a different parsing process @@ -145,7 +123,7 @@ class Pass { const passBuffer = this._extractPassDefinition(); 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 => { 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 * and returning the compiled manifest @@ -331,7 +306,7 @@ class Pass { genericDebug(formatMessage("DATE_FORMAT_UNMATCH", "Relevant Date")); return this; } - + let dateParse = dateToW3CString(data, relevanceDateFormat); if (!dateParse) { @@ -506,23 +481,6 @@ class Pass { 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 * @@ -589,8 +547,9 @@ class Pass { */ signature.addSigner({ - key: this.Certificates.signerKey, + key: this.Certificates.signerKey.keyFile, certificate: this.Certificates.signerCert, + digestAlgorithm: forge.pki.oids.sha1, authenticatedAttributes: [{ type: forge.pki.oids.contentType, value: forge.pki.oids.data @@ -731,7 +690,7 @@ class Pass { * @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") { // Nothing must be added. Void object is returned. return Promise.resolve({}); @@ -740,14 +699,14 @@ function readCertificates(certificates) { const raw = certificates._raw; const optCertsNames = Object.keys(raw); 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 const realRawValue = !(cert instanceof Object) ? cert : cert["keyFile"]; // We are checking if the string is a path or a content if (!!path.parse(realRawValue).ext) { const resolvedPath = path.resolve(realRawValue); - return readFile(resolvedPath); + return readFile(resolvedPath, { encoding: "utf8" }); } else { return Promise.resolve(realRawValue); } @@ -759,6 +718,7 @@ function readCertificates(certificates) { // which is conjoint later with the other pems return Object.assign( + {}, ...contents.map((file, index) => { const certName = optCertsNames[index]; const pem = parsePEM(certName, file, raw[certName].passphrase); @@ -825,5 +785,3 @@ function barcodesFromUncompleteData(origin) { ) ); } - -module.exports = { Pass };