UNPKG

@salesforce/core

Version:

Core libraries to interact with SFDX projects, orgs, and APIs.

319 lines (318 loc) 11.4 kB
/// <reference types="node" /> import { EventEmitter } from 'events'; import { AsyncOptionalCreatable, Duration, Env } from '@salesforce/kit/lib'; import { AnyFunction, AnyJson, JsonMap } from '@salesforce/ts-types/lib'; import { Org } from '../org'; import { StatusResult } from './client'; /** * Types for defining extensions. */ export interface StreamingExtension { /** * Extension for outgoing message. * * @param message The message. * @param callback The callback to invoke after the message is processed. */ outgoing?: (message: JsonMap, callback: AnyFunction) => void; /** * Extension for the incoming message. * * @param message The message. * @param callback The callback to invoke after the message is processed. */ incoming?: (message: JsonMap, callback: AnyFunction) => void; } /** * Function type for processing messages */ export declare type StreamProcessor = (message: JsonMap) => StatusResult; /** * Comet client interface. The is to allow for mocking the inner streaming Cometd implementation. * The Faye implementation is used by default but it could be used to adapt another Cometd impl. */ export declare abstract class CometClient extends EventEmitter { /** * Disable polling features. * * @param label Polling feature label. */ abstract disable(label: string): void; /** * Add a custom extension to the underlying client. * * @param extension The json function for the extension. */ abstract addExtension(extension: StreamingExtension): void; /** * Sets an http header name/value. * * @param name The header name. * @param value The header value. */ abstract setHeader(name: string, value: string): void; /** * handshake with the streaming channel * * @param callback Callback for the handshake when it successfully completes. The handshake should throw * errors when errors are encountered. */ abstract handshake(callback: () => void): void; /** * Subscribes to Comet topics. Subscribe should perform a handshake if one hasn't been performed yet. * * @param channel The topic to subscribe to. * @param callback The callback to execute once a message has been received. */ abstract subscribe(channel: string, callback: (message: JsonMap) => void): CometSubscription; /** * Method to call to disconnect the client from the server. */ abstract disconnect(): void; } /** * Inner streaming client interface. This implements the Cometd behavior. * Also allows for mocking the functional behavior. */ export interface StreamingClientIfc { /** * Returns a comet client implementation. * * @param url The target url of the streaming service endpoint. */ getCometClient: (url: string) => CometClient; /** * Sets the logger function for the CometClient. * * @param logLine A log message passed to the the assigned function. */ setLogger: (logLine: (message: string) => void) => void; } /** * The subscription object returned from the cometd subscribe object. */ export interface CometSubscription { callback(callback: () => void): void; errback(callback: (error: Error) => void): void; } /** * Api wrapper to support Salesforce streaming. The client contains an internal implementation of a cometd specification. * * Salesforce client and timeout information * * Streaming API imposes two timeouts, as supported in the Bayeux protocol. * * Socket timeout: 110 seconds * A client receives events (JSON-formatted HTTP responses) while it waits on a connection. If no events are generated * and the client is still waiting, the connection times out after 110 seconds and the server closes the connection. * Clients should reconnect before two minutes to avoid the connection timeout. * * Reconnect timeout: 40 seconds * After receiving the events, a client needs to reconnect to receive the next set of events. If the reconnection * doesn't happen within 40 seconds, the server expires the subscription and the connection is closed. If this happens, * the client must start again and handshake, subscribe, and connect. Each Streaming API client logs into an instance * and maintains a session. When the client handshakes, connects, or subscribes, the session timeout is restarted. A * client session times out if the client doesn’t reconnect to the server within 40 seconds after receiving a response * (an event, subscribe result, and so on). * * Note that these timeouts apply to the Streaming API client session and not the Salesforce authentication session. If * the client session times out, the authentication session remains active until the organization-specific timeout * policy goes into effect. * * ``` * const streamProcessor = (message: JsonMap): StatusResult => { * const payload = ensureJsonMap(message.payload); * const id = ensureString(payload.id); * * if (payload.status !== 'Active') { * return { completed: false }; * } * * return { * completed: true, * payload: id * }; * }; * * const org = await Org.create({}); * const options = new StreamingClient.DefaultOptions(org, 'MyPushTopics', streamProcessor); * * const asyncStatusClient = await StreamingClient.create(options); * * await asyncStatusClient.handshake(); * * const info: RequestInfo = { * method: 'POST', * url: `${org.getField(OrgFields.INSTANCE_URL)}/SomeService`, * headers: { HEADER: 'HEADER_VALUE'}, * body: 'My content' * }; * * await asyncStatusClient.subscribe(async () => { * const connection = await org.getConnection(); * // Now that we are subscribed, we can initiate the request that will cause the events to start streaming. * const requestResponse: JsonCollection = await connection.request(info); * const id = ensureJsonMap(requestResponse).id; * console.log(`this.id: ${JSON.stringify(ensureString(id), null, 4)}`); * }); * ``` */ export declare class StreamingClient extends AsyncOptionalCreatable<StreamingClient.Options> { private readonly targetUrl; private readonly options; private logger; private cometClient; /** * Constructor * * @param options Streaming client options * {@link AsyncCreatable.create} */ constructor(options?: StreamingClient.Options); /** * Asynchronous initializer. */ init(): Promise<void>; /** * Allows replaying of of Streaming events starting with replayId. * * @param replayId The starting message id to replay from. */ replay(replayId: number): void; /** * Provides a convenient way to handshake with the server endpoint before trying to subscribe. */ handshake(): Promise<StreamingClient.ConnectionState>; /** * Subscribe to streaming events. When the streaming processor that's set in the options completes execution it * returns a payload in the StatusResult object. The payload is just echoed here for convenience. * * **Throws** *{@link SfdxError}{ name: '{@link StreamingClient.TimeoutErrorType.SUBSCRIBE}'}* When the subscribe timeout occurs. * * @param streamInit This function should call the platform apis that result in streaming updates on push topics. * {@link StatusResult} */ subscribe(streamInit?: () => Promise<void>): Promise<AnyJson | void>; /** * Handler for incoming streaming messages. * * @param message The message to process. * @param cb The callback. Failure to call this can cause the internal comet client to hang. */ private incoming; private doTimeout; private disconnectClient; private disconnect; /** * Simple inner log wrapper * * @param message The message to log */ private log; } export declare namespace StreamingClient { /** * Options for the StreamingClient * * @interface */ interface Options { /** * The org streaming target. */ org: Org; /** * The hard timeout that happens with subscribe */ subscribeTimeout: Duration; /** * The hard timeout that happens with a handshake. */ handshakeTimeout: Duration; /** * The streaming channel aka topic */ channel: string; /** * The salesforce api version */ apiVersion: string; /** * The function for processing streaming messages */ streamProcessor: StreamProcessor; /** * The function for build the inner client impl. Allows for mocking. */ streamingImpl: StreamingClientIfc; } /** * Default Streaming Options. Uses Faye as the cometd impl. */ class DefaultOptions implements StreamingClient.Options { static readonly SFDX_ENABLE_FAYE_COOKIES_ALLOW_ALL_PATHS = "SFDX_ENABLE_FAYE_REQUEST_RESPONSE_LOGGING"; static readonly SFDX_ENABLE_FAYE_REQUEST_RESPONSE_LOGGING = "SFDX_ENABLE_FAYE_REQUEST_RESPONSE_LOGGING"; static readonly DEFAULT_SUBSCRIBE_TIMEOUT: Duration; static readonly DEFAULT_HANDSHAKE_TIMEOUT: Duration; apiVersion: string; org: Org; streamProcessor: StreamProcessor; subscribeTimeout: Duration; handshakeTimeout: Duration; channel: string; streamingImpl: StreamingClientIfc; /** * Constructor for DefaultStreamingOptions * * @param org The streaming target org * @param channel The streaming channel or topic. If the topic is a system topic then api 36.0 is used. * System topics are deprecated. * @param streamProcessor The function called that can process streaming messages. * @param envDep * @see {@link StatusResult} */ constructor(org: Org, channel: string, streamProcessor: StreamProcessor, envDep?: Env); /** * Setter for the subscribe timeout. * * **Throws** An error if the newTime is less than the default time. * * @param newTime The new subscribe timeout. * {@link DefaultOptions.DEFAULT_SUBSCRIBE_TIMEOUT} */ setSubscribeTimeout(newTime: Duration): void; /** * Setter for the handshake timeout. * * **Throws** An error if the newTime is less than the default time. * * @param newTime The new handshake timeout * {@link DefaultOptions.DEFAULT_HANDSHAKE_TIMEOUT} */ setHandshakeTimeout(newTime: Duration): void; } /** * Connection state * * @see {@link StreamingClient.handshake} */ enum ConnectionState { /** * Used to indicated that the streaming client is connected. */ CONNECTED = 0 } /** * Indicators to test error names for StreamingTimeouts */ enum TimeoutErrorType { /** * To indicate the error occurred on handshake */ HANDSHAKE = "genericHandshakeTimeoutMessage", /** * To indicate the error occurred on subscribe */ SUBSCRIBE = "genericTimeoutMessage" } }