UNPKG

pubnub

Version:

Publish & Subscribe Real-time Messaging with PubNub

207 lines (173 loc) 5.54 kB
/** * Get history REST API module. * * @internal */ import { TransportResponse } from '../../types/transport-response'; import { ICryptoModule } from '../../interfaces/crypto-module'; import { AbstractRequest } from '../../components/request'; import RequestOperation from '../../constants/operations'; import { KeySet, Payload, Query } from '../../types/api'; import * as History from '../../types/api/history'; import { encodeString } from '../../utils'; // -------------------------------------------------------- // ---------------------- Defaults ------------------------ // -------------------------------------------------------- // region Defaults /** * Whether verbose logging enabled or not. */ const LOG_VERBOSITY = false; /** * Whether associated message metadata should be returned or not. */ const INCLUDE_METADATA = false; /** * Whether timetokens should be returned as strings by default or not. */ const STRINGIFY_TIMETOKENS = false; /** * Default and maximum number of messages which should be returned. */ const MESSAGES_COUNT = 100; // endregion // -------------------------------------------------------- // ------------------------ Types ------------------------- // -------------------------------------------------------- // region Types /** * Request configuration parameters. */ type RequestParameters = History.GetHistoryParameters & { /** * PubNub REST API access key set. */ keySet: KeySet; /** * Published data encryption module. */ crypto?: ICryptoModule; /** * Whether verbose logging enabled or not. * * @default `false` */ logVerbosity?: boolean; }; /** * Service success response. */ type ServiceResponse = [ /** * List of previously published messages. */ { /** * Message payload (decrypted). */ message: Payload; /** * When message has been received by PubNub service. */ timetoken: string | number; /** * Additional data which has been published along with message to be used with real-time * events filter expression. */ meta?: Payload; }[], /** * Received messages timeline start. */ string | number, /** * Received messages timeline end. */ string | number, ]; // endregion /** * Get single channel messages request. * * @internal */ export class GetHistoryRequest extends AbstractRequest<History.GetHistoryResponse, ServiceResponse> { constructor(private readonly parameters: RequestParameters) { super(); // Apply defaults. if (parameters.count) parameters.count = Math.min(parameters.count, MESSAGES_COUNT); else parameters.count = MESSAGES_COUNT; parameters.stringifiedTimeToken ??= STRINGIFY_TIMETOKENS; parameters.includeMeta ??= INCLUDE_METADATA; parameters.logVerbosity ??= LOG_VERBOSITY; } operation(): RequestOperation { return RequestOperation.PNHistoryOperation; } validate(): string | undefined { if (!this.parameters.keySet.subscribeKey) return 'Missing Subscribe Key'; if (!this.parameters.channel) return 'Missing channel'; } async parse(response: TransportResponse): Promise<History.GetHistoryResponse> { const serviceResponse = this.deserializeResponse(response); const messages = serviceResponse[0]; const startTimeToken = serviceResponse[1]; const endTimeToken = serviceResponse[2]; // Handle malformed get history response. if (!Array.isArray(messages)) return { messages: [], startTimeToken, endTimeToken }; return { messages: messages.map((payload) => { const processedPayload = this.processPayload(payload.message); const item: History.GetHistoryResponse['messages'][number] = { entry: processedPayload.payload, timetoken: payload.timetoken, }; if (processedPayload.error) item.error = processedPayload.error; if (payload.meta) item.meta = payload.meta; return item; }), startTimeToken, endTimeToken, }; } protected get path(): string { const { keySet: { subscribeKey }, channel, } = this.parameters; return `/v2/history/sub-key/${subscribeKey}/channel/${encodeString(channel)}`; } protected get queryParameters(): Query { const { start, end, reverse, count, stringifiedTimeToken, includeMeta } = this.parameters; return { count: count!, include_token: 'true', ...(start ? { start } : {}), ...(end ? { end } : {}), ...(stringifiedTimeToken! ? { string_message_token: 'true' } : {}), ...(reverse !== undefined && reverse !== null ? { reverse: reverse.toString() } : {}), ...(includeMeta! ? { include_meta: 'true' } : {}), }; } private processPayload(payload: Payload): { payload: Payload; error?: string } { const { crypto, logVerbosity } = this.parameters; if (!crypto || typeof payload !== 'string') return { payload }; let decryptedPayload: string; let error: string | undefined; try { const decryptedData = crypto.decrypt(payload); decryptedPayload = decryptedData instanceof ArrayBuffer ? JSON.parse(GetHistoryRequest.decoder.decode(decryptedData)) : decryptedData; } catch (err) { if (logVerbosity!) console.log(`decryption error`, (err as Error).message); decryptedPayload = payload; error = `Error while decrypting message content: ${(err as Error).message}`; } return { payload: decryptedPayload, error, }; } }