UNPKG

@basetime/a2w-api-ts

Version:

Client library that communicates with the addtowallet API.

810 lines (797 loc) 21.1 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { CampaignsEndpoint: () => CampaignsEndpoint, ClaimsEndpoint: () => ClaimsEndpoint, Client: () => Client, KeysProvider: () => KeysProvider, OAuthProvider: () => OAuthProvider, OrganizationsEndpoint: () => OrganizationsEndpoint, StoredProvider: () => StoredProvider, TemplatesEndpoint: () => TemplatesEndpoint }); module.exports = __toCommonJS(src_exports); // src/NoopLogger.ts var NoopLogger = class { /** * @inheritDoc */ debug(message, meta) { } /** * @inheritDoc */ info(message, meta) { } /** * @inheritDoc */ error(message, meta) { } }; // src/constants.ts var baseUrl = void 0; var getBaseUrl = () => { if (baseUrl) { return baseUrl; } return "https://app.addtowallet.io/api/v1"; }; var setBaseUrl = (url) => { baseUrl = url; }; // src/endpoint/Endpoint.ts var Endpoint = class { /** * Constructor. * * @param req The object to use to make requests. */ constructor(req) { this.req = req; } /** * Makes a GET request. * * @param url The url to fetch. */ doGet = async (url, authenticate = true) => { return await this.req.fetch( url, { method: "GET" }, authenticate ); }; /** * Makes a POST request. * * @param url The url to fetch. * @param body The body to send. */ doPost = async (url, body, authenticate = true) => { const options = { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json" }, body: JSON.stringify(body) }; return await this.req.fetch(url, options, authenticate); }; /** * Makes a DELETE request. * * @param url The url to fetch. */ doDelete = async (url, authenticate = true) => { return await this.req.fetch( url, { method: "DELETE" }, authenticate ); }; }; // src/endpoint/CampaignsEndpoint.ts var endpoint = "/campaigns"; var enrollmentEndpoint = "/e"; var CampaignsEndpoint = class extends Endpoint { /** * A function to encode the data into a jwt. Used by the enrollment endpoint. */ jwtEncode; /** * Returns all of the campaigns for authenticated organization. * * @returns The campaigns. */ getAll = async () => { return await this.doGet(endpoint); }; /** * Returns the details of a campaign. * * @param id The ID of the campaign. */ getById = async (id) => { return await this.doGet(`${endpoint}/${id}`); }; /** * Returns the passes for a campaign. * * @param campaignId The ID of the campaign. * @returns The passes. */ getPasses = async (campaignId) => { return await this.doGet(`${endpoint}/${campaignId}/passes`); }; /** * Returns the details for a pass. * * @param campaignId The campaign the pass belongs to. * @param passId The ID of the pass. * @param scanner Only used by scanners. The scanner that's being used to request the pass. */ getPass = async (campaignId, passId, scanner = "") => { const scannerStr = encodeURIComponent(JSON.stringify(scanner)); const url = `${endpoint}/${campaignId}/passes/details/${passId}?scanner=${scannerStr}`; return await this.doGet(url); }; /** * Updates the details of a pass. * * This method also updates the wallets that contain the pass. * * @param campaignId The ID of the campaign the pass belongs to. * @param passId The ID of the pass. * @param body The new pass values. */ updatePass = async (campaignId, passId, body) => { const url = `${endpoint}/${campaignId}/passes/details/${passId}`; return await this.doPost(url, body); }; /** * Appends a log to a pass. * * @param campaignId The ID of the campaign the pass belongs to. * @param passId The ID of the pass. * @param log The message to append to the log. */ appendLog = async (campaignId, passId, log) => { const url = `${endpoint}/${campaignId}/passes/${passId}/logs`; return await this.doPost(url, { log }); }; /** * Creates a pass bundle and returns the URL to the claims page. * * Example: * ```ts * const client = new Client(auth, console); * const link = await client.campaigns.createBundle('123'); * console.log(link); * ``` * * @param campaignId The campaign the pass belongs to. * @param metaValues The meta values to set. * @param formValues The form values to set. * @param utm The UTM values to pass along to the api. */ createBundle = async (campaignId, metaValues = {}, formValues = {}, utm = {}) => { const url = `${endpoint}/${campaignId}/passes/bundle`; return await this.doPost(url, { metaValues, formValues, utm }); }; /** * Creates an enrollment for a campaign, and returns the bundle ID and any errors. * * This method needs to encode the data into a jwt. The jwt is used to authenticate * with the site. This method requires the jwtEncode function to be set. * * @param campaignId The ID of the campaign. * @param metaValues The meta values to set. * @param formValues The form values to set. */ createEnrollment = async (campaignId, metaValues = {}, formValues = {}) => { if (!this.jwtEncode) { throw new Error( "CampaignsEndpoint.createEnrollment() requires the jwtEncode function to be set." ); } const body = { d: await this.jwtEncode({ metaValues, formValues }) }; const url = `${enrollmentEndpoint}/campaign/${campaignId}`; return await this.doPost(url, body); }; /** * Returns the passes for a job. * * @param campaignId The ID of the campaign. * @param jobId The ID of the job. * @returns The passes. */ getPassesByJob = async (campaignId, jobId) => { return await this.doGet(`${endpoint}/${campaignId}/passes/${jobId}`); }; /** * Returns the claims for a campaign. * * @param campaignId The ID of the campaign. * @returns The claims. */ getClaims = async (campaignId) => { return await this.doGet(`${endpoint}/${campaignId}/claims`); }; /** * Returns the jobs for a campaign. * * @param campaignId The ID of the campaign. * @returns The jobs. */ getJobs = async (campaignId) => { return await this.doGet(`${endpoint}/${campaignId}/jobs`); }; /** * Returns statistics for a campaign. * * @param campaignId The ID of the campaign. * @returns The statistics. */ getStats = async (campaignId) => { return await this.doGet(`${endpoint}/${campaignId}/stats`); }; /** * Returns the enrollments for a campaign. * * @param campaignId The ID of the campaign. * @returns The enrollments. */ getEnrollments = async (campaignId) => { return await this.doGet(`${endpoint}/${campaignId}/enrollments`); }; /** * Sets the redeemed status of a pass to true. * * @param campaignId The ID of the campaign. * @param passId The ID of the pass. * @returns True if the pass was redeemed, false if it was already redeemed. */ redeemPass = async (campaignId, passId) => { const url = `${endpoint}/${campaignId}/passes/${passId}/redeemed`; return await this.doPost(url, {}); }; /** * Returns the redeemed status of a pass. * * @param campaignId The ID of the campaign. * @param passId The ID of the pass. * @returns The redeemed status. */ getRedeemedStatus = async (campaignId, passId) => { const url = `${endpoint}/${campaignId}/passes/${passId}/redeemed`; return await this.doGet(url); }; }; // src/endpoint/ClaimsEndpoint.ts var endpoint2 = "/claim"; var ClaimsEndpoint = class extends Endpoint { /** * Returns the pkpass file for a campaign and pass. * * @param campaignId The ID of the campaign. * @param passId The ID of the pass. * @returns The pkpass file. */ getPkpass = async (campaignId, passId) => { const url = `${endpoint2}/${campaignId}/${passId}.pkpass`; return await this.req.fetch(url, { method: "GET", headers: { Accept: "application/vnd.apple.pkpass" } }); }; }; // src/endpoint/OrganizationsEndpoint.ts var endpoint3 = "/organization"; var OrganizationsEndpoint = class extends Endpoint { /** * Returns the authenticated organization. * * @returns The organization. */ getMine = async () => { return await this.doGet(endpoint3); }; /** * Returns a scanner invite by code. * * @param code The invite code. */ getScannerInvite = async (code) => { return await this.doGet(`${endpoint3}/scanners/invites/${code}`, false); }; /** * Begins the scanner exchange. * * @param code The invite code. */ startScannerExchange = async (code) => { return await this.doGet(`${endpoint3}/scanners/invites/${code}/start`, false); }; /** * Accepts an scanner app invite code and returns api keys. * * @param code The invite code. * @param pushToken The push token. * @param scannerDeviceInfo The scanner device info. */ finishScannerExchange = async (code, pushToken, scannerDeviceInfo) => { return await this.doPost( `${endpoint3}/scanners/invites`, { code, pushToken, scannerDeviceInfo }, false ); }; /** * Returns the API keys for the authenticated organization. */ getApiKeys = async () => { return await this.doGet(`${endpoint3}/apiKeys`); }; /** * Returns an API key by ID. * * @param id The ID of the API key. */ getApiKey = async (id, scanner = "") => { const scannerStr = encodeURIComponent(JSON.stringify(scanner)); const url = `${endpoint3}/apiKeys/${id}?scanner=${scannerStr}`; return await this.doGet(url); }; /** * Deletes an API key. * * @param id The ID of the API key. */ deleteApiKey = async (id) => { return await this.doDelete(`${endpoint3}/apiKeys/${id}`); }; }; // src/endpoint/TemplatesEndpoint.ts var endpoint4 = "/templates"; var TemplatesEndpoint = class extends Endpoint { /** * Returns a template by ID. * * @param id The ID of the template. */ getById = async (id) => { const url = `${endpoint4}/simple/${id}`; return await this.doGet(url); }; /** * Returns all of the templates for authenticated organization. * * @returns The templates. */ getAll = async () => { return await this.doGet(`${endpoint4}/organization`); }; /** * Returns all of the templates for a specific tag. * * @param tag The tag. * @returns The templates. */ getByTag = async (tag) => { return await this.doGet(`${endpoint4}/tagged/${tag}`); }; }; // src/Client.ts var Client = class { /** * The authentication object. */ auth; /** * The logger. */ logger; /** * The campaigns endpoint. */ _campaigns; /** * The claims endpoint. */ _claims; /** * The templates endpoint. */ _templates; /** * The organizations endpoint. */ _organizations; /** * Constructor. * * @param auth The authentication provider. * @param logger The logger to use. */ constructor(auth, logger) { this.logger = logger || new NoopLogger(); if (auth) { this.setAuth(auth); } } /** * Sets the base URL for all requests to the API. * * @param url The base URL for all requests to the API. */ setBaseUrl = (url) => { setBaseUrl(url); }; /** * Sets the auth provider to use. * * @param auth The auth provider to use. */ setAuth = (auth) => { this.auth = auth; this.auth.setLogger(this.logger); }; /** * Returns the campaigns endpoint. * * @returns {CampaignsEndpoint} The campaigns endoint. */ get campaigns() { if (!this._campaigns) { this._campaigns = new CampaignsEndpoint(this); } return this._campaigns; } /** * Returns the claims endpoint. * * @returns {ClaimsEndpoint} The claims endpoint. */ get claims() { if (!this._claims) { this._claims = new ClaimsEndpoint(this); } return this._claims; } /** * Returns the templates endpoint. */ get templates() { if (!this._templates) { this._templates = new TemplatesEndpoint(this); } return this._templates; } /** * Returns the organizations endpoint. */ get organizations() { if (!this._organizations) { this._organizations = new OrganizationsEndpoint(this); } return this._organizations; } /** * Sends a request using the fetcher and returns the response. * * Adds the bearer token to the headers and catches errors. * * @param url The url to send the request to. * @param options The fetch options. * @param authenticate Whether to authenticate the request. * @returns The response from the endpoint. */ fetch = async (url, options = {}, authenticate = true) => { const sep = url.includes("?") ? "&" : "?"; url = `${getBaseUrl()}${url}${sep}api=true`; const headers = options.headers ? new Headers(options.headers) : new Headers(); if (!headers.has("Accept")) { headers.set("Accept", "application/json"); } if (!headers.has("Content-Type")) { headers.set("Content-Type", "application/json"); } if (authenticate && this.auth) { const bearerToken = await this.auth.authenticate(); headers.set("Authorization", `Bearer ${bearerToken}`); } this.logger.debug( `${(options == null ? void 0 : options.method) || "GET"} ${url}, ${authenticate ? "authenticate" : "no authenticate"}` ); const opts = { ...options, headers }; return await fetch(url, opts).then(async (resp) => { if (resp.ok) { if (headers.get("Accept") === "application/json") { return resp.json(); } return resp.text(); } const body = await resp.text(); let json = body; try { json = JSON.parse(body); } catch (err) { } if (typeof json === "string") { throw new Error(`Response failed: ${resp.status} ${body}`); } if (json.error) { throw new Error(`${resp.status} ${json.error}`); } throw new Error(`Response failed: ${resp.status} ${resp.statusText}`); }).catch((err) => { throw new Error(`Response failed: ${err.toString()}`); }); }; }; // src/provider/KeysProvider.ts var KeysProvider = class { /** * Constructor. * * @param key The API key. * @param secret The API secret. * @param logger The logger to use. */ constructor(key, secret, logger) { this.key = key; this.secret = secret; this.logger = logger || new NoopLogger(); } /** * The successful last authentication. */ authed; /** * The logger. */ logger; /** * @inheritdoc */ setLogger = (logger) => { this.logger = logger; }; /** * @inheritdoc */ getAuthed = () => { return this.authed; }; /** * @inheritdoc */ authenticate = async () => { if (this.authed && this.authed.expiresAt > Date.now() / 1e3) { return this.authed.idToken; } const opts = { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json" }, body: JSON.stringify({ key: this.key, secret: this.secret }) }; const baseUrl2 = getBaseUrl(); this.logger.debug(`Sending request to ${baseUrl2}/auth/apiGrant`); this.authed = await fetch(`${baseUrl2}/auth/apiGrant`, opts).then(async (resp) => { if (resp.ok) { return resp.json(); } throw new Error( `Authentication returned non-ok response: ${resp.status} ${resp.statusText}` ); }).then((json) => { if (typeof json !== "object") { throw new Error("Invalid object from /auth/apiGrant endpoint."); } if (typeof json.idToken !== "string") { throw new Error("Invalid idToken from /auth/apiGrant endpoint."); } if (typeof json.refreshToken !== "string") { throw new Error("Invalid refreshToken from /auth/apiGrant endpoint."); } if (typeof json.expiresAt !== "number") { throw new Error("Invalid expiresAt from /auth/apiGrant endpoint."); } return json; }).catch((err) => { throw new Error(`Failed to authenticate: ${err.toString()}`); }); return this.authed.idToken; }; }; // src/provider/OAuthProvider.ts var e = encodeURIComponent; var OAuthProvider = class { /** * Constructor. * * @param app The ID of the app requesting authentication. * @param code The code that was received from the oauth. * @param logger The logger to use. */ constructor(app, code = "", logger) { this.app = app; this.code = code; this.logger = logger || new NoopLogger(); } /** * The successful last authentication. */ authed; /** * The logger. */ logger; /** * @inheritdoc */ setLogger = (logger) => { this.logger = logger; }; /** * @inheritdoc */ getAuthed = () => { return this.authed; }; /** * Returns a URL to get an oauth code. * * @param redirectUrl The URL to redirect to after the oauth code is received. * @param scopes The requested scopes. * @param state Any value, it will be returned in the redirect. */ getCodeUrl = (redirectUrl, scopes, state) => { this.logger.debug("OAuth.getCodeUrl", { redirectUrl, scopes, state }); return `${getBaseUrl()}/auth/oauth/code?app=${e(this.app)}&redirectUrl=${e(redirectUrl)}&scope=${e(scopes.join(" "))}&state=${e(state)}`; }; /** * @inheritdoc */ authenticate = async () => { if (this.authed && this.authed.expiresAt > Date.now() / 1e3) { return this.authed.idToken; } const opts = { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json" }, body: JSON.stringify({ app: this.app, code: this.code }) }; const baseUrl2 = getBaseUrl(); this.logger.debug(`Sending request to ${baseUrl2}/auth/oauth/token`); this.authed = await fetch(`${baseUrl2}/auth/oauth/token`, opts).then((resp) => { if (resp.ok) { return resp.json(); } throw new Error( `Authentication returned non-ok response: ${resp.status} ${resp.statusText}` ); }).then((json) => { if (typeof json !== "object") { throw new Error("Invalid object from /oauth/token endpoint."); } if (typeof json.idToken !== "string") { throw new Error("Invalid idToken from /oauth/token endpoint."); } if (typeof json.refreshToken !== "string") { throw new Error("Invalid refreshToken from /oauth/token endpoint."); } if (typeof json.expiresAt !== "number") { throw new Error("Invalid expiresAt from /oauth/token endpoint."); } return json; }).catch((err) => { throw new Error(`Failed to authenticate: ${err.toString()}`); }); return this.authed.idToken; }; }; // src/provider/StoredProvider.ts var StoredProvider = class { /** * Constructor. * * @param authed The auth credentials. * @param logger The logger to use. */ constructor(authed, logger) { this.authed = authed; this.logger = logger || new NoopLogger(); } /** * The logger. */ logger; /** * @inheritdoc */ setLogger = (logger) => { this.logger = logger; }; /** * @inheritdoc */ getAuthed = () => { return this.authed; }; /** * @inheritdoc */ authenticate = async () => { if (this.authed && this.authed.expiresAt > Date.now() / 1e3) { return this.authed.idToken; } this.logger.error("StoredProvider: No valid authed found"); throw new Error("StoredProvider: No valid authed found"); }; }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { CampaignsEndpoint, ClaimsEndpoint, Client, KeysProvider, OAuthProvider, OrganizationsEndpoint, StoredProvider, TemplatesEndpoint });