UNPKG

jsforce

Version:

Salesforce API Library for JavaScript

253 lines (252 loc) 8.55 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.StreamingExtension = exports.Streaming = exports.Subscription = exports.Client = void 0; /** * @file Manages Streaming APIs * @author Shinichi Tomita <shinichi.tomita@gmail.com> */ const events_1 = require("events"); const faye_1 = require("faye"); Object.defineProperty(exports, "Client", { enumerable: true, get: function () { return faye_1.Client; } }); Object.defineProperty(exports, "Subscription", { enumerable: true, get: function () { return faye_1.Subscription; } }); const jsforce_1 = require("../jsforce"); const StreamingExtension = __importStar(require("./streaming/extension")); exports.StreamingExtension = StreamingExtension; /*--------------------------------------------*/ /** * Streaming API topic class */ class Topic { _streaming; name; constructor(streaming, name) { this._streaming = streaming; this.name = name; } /** * Subscribe listener to topic */ subscribe(listener) { return this._streaming.subscribe(this.name, listener); } /** * Unsubscribe listener from topic */ unsubscribe(subscr) { this._streaming.unsubscribe(this.name, subscr); return this; } } /*--------------------------------------------*/ /** * Streaming API Generic Streaming Channel */ class Channel { _streaming; _id; name; constructor(streaming, name) { this._streaming = streaming; this.name = name; } /** * Subscribe to channel */ subscribe(listener) { return this._streaming.subscribe(this.name, listener); } unsubscribe(subscr) { this._streaming.unsubscribe(this.name, subscr); return this; } async push(events) { const isArray = Array.isArray(events); const pushEvents = Array.isArray(events) ? events : [events]; const conn = this._streaming._conn; if (!this._id) { this._id = conn .sobject('StreamingChannel') .findOne({ Name: this.name }, ['Id']) .then((rec) => rec?.Id); } const id = await this._id; if (!id) { throw new Error(`No streaming channel available for name: ${this.name}`); } const channelUrl = `/sobjects/StreamingChannel/${id}/push`; const rets = await conn.requestPost(channelUrl, { pushEvents, }); return isArray ? rets : rets[0]; } } /*--------------------------------------------*/ /** * Streaming API class */ class Streaming extends events_1.EventEmitter { _conn; _topics = {}; _fayeClients = {}; /** * */ constructor(conn) { super(); this._conn = conn; } /* @private */ _createClient(forChannelName, extensions) { // forChannelName is advisory, for an API workaround. It does not restrict or select the channel. const needsReplayFix = typeof forChannelName === 'string' && forChannelName.startsWith('/u/'); const endpointUrl = [ this._conn.instanceUrl, // special endpoint "/cometd/replay/xx.x" is only available in 36.0. // See https://releasenotes.docs.salesforce.com/en-us/summer16/release-notes/rn_api_streaming_classic_replay.htm 'cometd' + (needsReplayFix && this._conn.version === '36.0' ? '/replay' : ''), this._conn.version, ].join('/'); const fayeClient = new faye_1.Client(endpointUrl, {}); fayeClient.setHeader('Authorization', 'OAuth ' + this._conn.accessToken); if (Array.isArray(extensions)) { for (const extension of extensions) { fayeClient.addExtension(extension); } } // prevent streaming API server error const dispatcher = fayeClient._dispatcher; if (dispatcher.getConnectionTypes().indexOf('callback-polling') === -1) { dispatcher.selectTransport('long-polling'); dispatcher._transport.batching = false; } return fayeClient; } /** @private **/ _getFayeClient(channelName) { const isGeneric = channelName.startsWith('/u/'); const clientType = isGeneric ? 'generic' : 'pushTopic'; if (!this._fayeClients[clientType]) { this._fayeClients[clientType] = this._createClient(channelName); } return this._fayeClients[clientType]; } /** * Get named topic */ topic(name) { this._topics = this._topics || {}; const topic = (this._topics[name] = this._topics[name] || new Topic(this, name)); return topic; } /** * Get channel for channel name */ channel(name) { return new Channel(this, name); } /** * Subscribe topic/channel */ subscribe(name, listener) { const channelName = name.startsWith('/') ? name : '/topic/' + name; const fayeClient = this._getFayeClient(channelName); return fayeClient.subscribe(channelName, listener); } /** * Unsubscribe topic */ unsubscribe(name, subscription) { const channelName = name.startsWith('/') ? name : '/topic/' + name; const fayeClient = this._getFayeClient(channelName); fayeClient.unsubscribe(channelName, subscription); return this; } /** * Create a Streaming client, optionally with extensions * * See Faye docs for implementation details: https://faye.jcoglan.com/browser/extensions.html * * Example usage: * * ```javascript * const jsforce = require('jsforce'); * * // Establish a Salesforce connection. (Details elided) * const conn = new jsforce.Connection({ … }); * * const fayeClient = conn.streaming.createClient(); * * const subscription = fayeClient.subscribe(channel, data => { * console.log('topic received data', data); * }); * * subscription.cancel(); * ``` * * Example with extensions, using Replay & Auth Failure extensions in a server-side Node.js app: * * ```javascript * const jsforce = require('jsforce'); * const { StreamingExtension } = require('jsforce/api/streaming'); * * // Establish a Salesforce connection. (Details elided) * const conn = new jsforce.Connection({ … }); * * const channel = "/event/My_Event__e"; * const replayId = -2; // -2 is all retained events * * const exitCallback = () => process.exit(1); * const authFailureExt = new StreamingExtension.AuthFailure(exitCallback); * * const replayExt = new StreamingExtension.Replay(channel, replayId); * * const fayeClient = conn.streaming.createClient([ * authFailureExt, * replayExt * ]); * * const subscription = fayeClient.subscribe(channel, data => { * console.log('topic received data', data); * }); * * subscription.cancel(); * ``` */ createClient(extensions) { return this._createClient(null, extensions); } } exports.Streaming = Streaming; /*--------------------------------------------*/ /* * Register hook in connection instantiation for dynamically adding this API module features */ (0, jsforce_1.registerModule)('streaming', (conn) => new Streaming(conn)); exports.default = Streaming;