UNPKG

pubnub

Version:

Publish & Subscribe Real-time Messaging with PubNub

214 lines (184 loc) 5.95 kB
/** * Channels / channel groups presence REST API module. * * @internal */ import { TransportResponse } from '../../types/transport-response'; import { AbstractRequest } from '../../components/request'; import RequestOperation from '../../constants/operations'; import { KeySet, Payload, Query } from '../../types/api'; import * as Presence from '../../types/api/presence'; import { encodeNames } from '../../utils'; // -------------------------------------------------------- // ----------------------- Defaults ----------------------- // -------------------------------------------------------- // region Defaults /** * Whether `uuid` should be included in response or not. */ const INCLUDE_UUID = true; /** * Whether state associated with `uuid` should be included in response or not. */ const INCLUDE_STATE = false; // endregion // -------------------------------------------------------- // ------------------------ Types ------------------------- // -------------------------------------------------------- // region Types /** * Request configuration parameters. */ type RequestParameters = Presence.HereNowParameters & { /** * PubNub REST API access key set. */ keySet: KeySet; }; /** * Service success response. */ type BasicServiceResponse = { /** * Request result status code. */ status: number; /** * Here now human-readable result. */ message: string; /** * Name of the service which provided response. */ service: string; }; /** * Single channel here now service response. */ type SingleChannelServiceResponse = BasicServiceResponse & { /** * List of received channel subscribers. * * **Note:** Field is missing if `uuid` and `state` not included. */ uuids?: (string | { uuid: string; state?: Payload })[]; /** * Total number of active subscribers. */ occupancy: number; }; /** * Multiple channels / channel groups here now service response. */ type MultipleChannelServiceResponse = BasicServiceResponse & { /** * Retrieved channels' presence. */ payload: { /** * Total number of channels for which presence information received. */ total_channels: number; /** * Total occupancy for all retrieved channels. */ total_occupancy: number; /** * List of channels to which `uuid` currently subscribed. */ channels?: { [p: string]: { /** * List of received channel subscribers. * * **Note:** Field is missing if `uuid` and `state` not included. */ uuids: (string | { uuid: string; state?: Payload })[]; /** * Total number of active subscribers in single channel. */ occupancy: number; }; }; }; }; /** * Here now REST API service success response. */ type ServiceResponse = SingleChannelServiceResponse | MultipleChannelServiceResponse; // endregion /** * Channel presence request. * * @internal */ export class HereNowRequest extends AbstractRequest<Presence.HereNowResponse, ServiceResponse> { constructor(private readonly parameters: RequestParameters) { super(); // Apply defaults. this.parameters.queryParameters ??= {}; this.parameters.includeUUIDs ??= INCLUDE_UUID; this.parameters.includeState ??= INCLUDE_STATE; } operation(): RequestOperation { const { channels = [], channelGroups = [] } = this.parameters; return channels.length === 0 && channelGroups.length === 0 ? RequestOperation.PNGlobalHereNowOperation : RequestOperation.PNHereNowOperation; } validate(): string | undefined { if (!this.parameters.keySet.subscribeKey) return 'Missing Subscribe Key'; } async parse(response: TransportResponse): Promise<Presence.HereNowResponse> { const serviceResponse = this.deserializeResponse(response); // Extract general presence information. const totalChannels = 'occupancy' in serviceResponse ? 1 : serviceResponse.payload.total_channels; const totalOccupancy = 'occupancy' in serviceResponse ? serviceResponse.occupancy : serviceResponse.payload.total_occupancy; const channelsPresence: Presence.HereNowResponse['channels'] = {}; let channels: Required<MultipleChannelServiceResponse['payload']>['channels'] = {}; // Remap single channel presence to multiple channels presence response. if ('occupancy' in serviceResponse) { const channel = this.parameters.channels![0]; channels[channel] = { uuids: serviceResponse.uuids ?? [], occupancy: totalOccupancy }; } else channels = serviceResponse.payload.channels ?? {}; Object.keys(channels).forEach((channel) => { const channelEntry = channels[channel]; channelsPresence[channel] = { occupants: this.parameters.includeUUIDs! ? channelEntry.uuids.map((uuid) => { if (typeof uuid === 'string') return { uuid, state: null }; return uuid; }) : [], name: channel, occupancy: channelEntry.occupancy, }; }); return { totalChannels, totalOccupancy, channels: channelsPresence, }; } protected get path(): string { const { keySet: { subscribeKey }, channels, channelGroups, } = this.parameters; let path = `/v2/presence/sub-key/${subscribeKey}`; if ((channels && channels.length > 0) || (channelGroups && channelGroups.length > 0)) path += `/channel/${encodeNames(channels ?? [], ',')}`; return path; } protected get queryParameters(): Query { const { channelGroups, includeUUIDs, includeState, queryParameters } = this.parameters; return { ...(!includeUUIDs! ? { disable_uuids: '1' } : {}), ...((includeState ?? false) ? { state: '1' } : {}), ...(channelGroups && channelGroups.length > 0 ? { 'channel-group': channelGroups.join(',') } : {}), ...queryParameters!, }; } }